yurenery/laravel-core-kit

Main Core Code for starter project modules and extensions



Documentation

Core Kit

To start project you should have locally installed: php@^7.4|^8.0, composer. \ You can use google search with requests: linux php local install, brew install php.
After first setup project can work via Valet or docker containers.

Main Core Code for starter project modules and extensions

What is CoreKit micro service

After installing CoreKit microservice you will receive next services out of the box:

  • Authorization microservice. Sign-in, Sign-out, verification and password reset processes. Also, any other verification actions can be developed through Core Verification Broker.
  • User registration.
  • Profile get and update.
  • Backend management system for users.
  • Permissions and roles management system.
  • SPA application fake routing. CoreKit give you powerful feature to make fake routes for SPA endpoints.

How to install from private repository

!!!!Never commit the auth.json file to your repository. For docker containerization use Vault or other secure variables to store access token for a user.

  1. Run
    composer config --global --auth http-basic.repo.packagist.com AttraactGroupUser <request token from maintainer>
    
  2. Add this into your composer.json:
    {
        "repositories": [
                {"type": "composer", "url": "https://repo.packagist.com/attractgroup/"}
            ]
    }

Installation Guide

Step by step instructions, that will help you start a new project to build something amazing.

Directories and project environment

List of root directories for future project:

  • project-files - contain all code files.
  • docker - contains all scripts for docker containerization process.
  • docs - contain all readme.md doc files.
  • terraform - contain all terraform templates for AWS resources setup.

You can find full description about each directory setup below.

Replace directories and project names to the real project name.

Setup project files

Create root directory of the project. For example, attract-starter-kit:

mkdir -m 777 attract-starter-kit &&
cd attract-starter-kit

After that you are ready for project-files install:

laravel new project-files &&
cd project-files

Set up attract starter kit

TIMELESS - operations that will be changed after several steps.

Follow this TODO list to set up starter project:

  • Change default php requirements in composer.json to ^7.4|^8.0
  • Authorize your composer into private packagist
  • [TIMELESS] Configure DB_CONNECTION to use sqlite driver into your .env:
    DB_CONNECTION=sqlite
    DB_DATABASE=project_files # remove this row
    cd ./database && touch database.sqlite && cd ..
  • Disable mysql configuration strict mode to prevent errors on cross relations ordering in sextant operations:
    [
      'mysql' => [
          //...,
          'strict' => false,
          //...,
      ]
    ];
  • Run:
    composer require attract-cores/laravel-core-kit attract-cores/laravel-core-test-bench
  • Remove all default laravel migrations
  • Run vendor:publish operations to create configuration environment for new project:
     php artisan vendor:publish --tag=laravel-mail && 
     php artisan vendor:publish --tag=attract-core-kit-core-modules --force && 
     php artisan vendor:publish --tag=attract-core-kit-auth-migrations --force &&
     php artisan vendor:publish --tag=attract-core-verification-broker-migrations --force &&
     php artisan vendor:publish --tag=attract-core-kit-auth-seeders --force &&
     php artisan vendor:publish --tag=attract-core-kit-auth-tests &&
     php artisan vendor:publish --tag=attract-core-kit-tests &&  
     composer dump-autoload
  • Go into terraform directory and run:
    cd ../terraform/tf-s3 && ln -s ../vars/variables.tf variables.tf && cd ../../project-files &&
    cd ../terraform/s3 && ln -s ../vars/variables.tf variables.tf && cd ../../project-files
  • Update ./config/auth.php api driver from token to passport:
    [
        'api' => [
                    //'driver' => 'token', you can remove this row.
                    'driver' => 'passport',
                    'provider' => 'users',
                    'hash' => false,
                ],
    ];
  • Add 'read_write_timeout' => 0 to your ./config/database.php redis section:
    [
        'default' => [
                    'url'                => env('REDIS_URL'),
                    'host'               => env('REDIS_HOST', '127.0.0.1'),
                    'password'           => env('REDIS_PASSWORD', NULL),
                    'port'               => env('REDIS_PORT', '6379'),
                    'database'           => env('REDIS_DB', '0'),
                    'read_write_timeout' => 0, // Add this into each settings block to prevent errors - "error while reading line from the server."
                ],
    ];
  • Remove api.php and web.php route files.
  • Change verified middleware into Core version AttractCores\LaravelCoreKit\Http\Middleware\EnsureEmailIsVerified in Kernel.php:
    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
      //...,
      'verified' => \AttractCores\LaravelCoreKit\Http\Middleware\EnsureEmailIsVerified::class,
      //...,
    ];
  • Copy new app key(APP_KEY) from .env into .env.example and .env.dev
  • Copy .env.example -> .env. If you will use docker skip this row. If you are using Laravel Valet, set up variables for your local environment.
  • Update DatabaseSeeder via:
    $this->call([
      	    DefaultAdminSeeder::class,
      	    DefaultRolesAndPermissionsSeeder::class
      	]);
  • If you will add new permissions/roles then you should extend AttractCores\LaravelCoreAuth\Database\Seeders\DefaultRolesAndPermissionsSeeder class and add new models like parent class do. For example:
        namespace Database\Seeders;
        
        use App\Models\Permission;
        use App\Models\Role;
        use AttractCores\LaravelCoreAuth\Database\Seeders\DefaultRolesAndPermissionsSeeder as CoreDefaultRolesAndPermissionsSeeder;
        use AttractCores\LaravelCoreAuth\Resolvers\CorePermission;
        use AttractCores\LaravelCoreAuth\Resolvers\CoreRole;
        
        /**
         * Class DefaultRolesAndPermissionsSeeder
         *
         * @package AttractCores\LaravelCoreAuth\Database\Seeders\Publishes
         * Date: 16.12.2020
         * Version: 1.0
         * Author: Yure Nery <yurenery@gmail.com>
         */
        class DefaultRolesAndPermissionsSeeder extends CoreDefaultRolesAndPermissionsSeeder
        {
            /**
             * Seed the application's database.
             *
             * @return void
             */
            public function run()
            {
                parent::run();
        
                $permissions = CorePermission::all();
                $permissionSlugFieldName = CorePermission::getSlugField();
        
                if ( ! $permissions->contains($permissionSlugFieldName, Permission::CAN_ORGANIZATION_ACCESS) ) {
        
                    CorePermission::factory()
                                  ->createOne([ 'name_en' => 'Can have Organisation access', 'slug' => Permission::CAN_ORGANIZATION_ACCESS ]);
        
                    CoreRole::factory()
                            ->createOne([ 'name_en' => 'Organisation Access', 'slug' => Role::CAN_ORGANIZATION ])
                            ->permissions()
                            ->sync([ 6 ]);
                }
            }
        }
  • You require to follow docker environment docs or use Laravel Valet for local serving.
  • If you are using Laravel Valet, then follow below steps:
    • Update migrations if needed and run below command if you are using Laravel Valet:
      php artisan migrate --seed
    • After migrations processed we should create passport keys and clients:
      php artisan passport:keys --force &&
      php artisan passport:client --client --no-interaction &&
      php artisan passport:client --password --no-interaction
  • Serve app via docker or Laravel Valet and move farther.
  • Update APP_KIT_AUTH_PASSWORD_GRANT_CLIENT_ID and APP_KIT_AUTH_PASSWORD_GRANT_CLIENT_SECRET variables values in .env. Update them in ./docker/local/envs/{YOUR_ENV} if you are using docker.
  • In case of changes inside exception handler and validation messages structure we need add some updates into tests, to prevent failing:
    • Update OauthTest.php class by adding this function:
    /**
     * Return status for handler catchers.
     *
     * @return int
     */
    protected function getCantLoginStatus()
    {
        return 401;
    }
    • Update RegisterTest.php adn add replace of testApiRegistrationValidation function:
    /**
     * Test api registration validation.
     *
     * @return void
     * @throws \Throwable
     */
    public function testApiRegistrationValidation()
    {
        $notUnique = $this->getTestRegisterData(false, 5);
        $response = $this->withHeaders([ 'Authorization' => $this->getBearerClientToken() ])
                         ->json('POST', $this->getRegisterRoute(), $notUnique);
    
        $response->assertStatus(422);
        $errors = collect($response->decodeResponseJson()->json('errors'));
        $this->assertEquals(2, count($errors));
        $this->assertTrue($errors->contains('field', 'password'));
        $this->assertTrue($errors->contains('field', 'email'));
    }    
  • Remove ./tests/Feature/ExampleTest.php file.
  • For DEV/STAGE environment setup use this docs.
  • After all actions, just run below command inside docker container or in project-files root(if using Valet):
    ./vendor/bin/phpunit --stop-on-failur
  • If green status obtained, starter kit ready to extend.

Extension possibilities

Any core request, controller, resource or library can be easily extended via Laravel service providers bind features.

After vendor:publish process your AppServiceProvider already include all necessary functions for extension.

User resource extension

After CoreKit installation project will grow, so we will need to extend default user resource with new relation expands. Let's do this.

For example: we need to separate fist_name and last_name fields. Don't forget to update the migration.

namespace App\Http\Resources;

use AttractCores\LaravelCoreKit\Http\Resources\UserResource as CoreUserResource;
use Illuminate\Support\Arr;

/**
 * Class UserResource
 *
 * @property \App\Models\User $resource
 *
 * @package App\Http\Resources\PublicResources
 * Date: 17.12.2020
 * Version: 1.0
 * Author: Yure Nery <yurenery@gmail.com>
 */
class UserResource extends CoreUserResource
{

    /**
     * Transform the resource into an array.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return array
     */
    public function toArray($request)
    {
        $parentData = Arr::except(parent::toArray($request), [ 'name' ]);

        return array_merge(
            $parentData, [
            'first_name'                  => $this->resource->first_name,
            'last_name'                   => $this->resource->last_name,
            'relations'                   => array_merge($parentData[ 'relations' ], [
                'avatar'             => $this->whenLoaded('avatar', function () {
                    return new MediaResource($this->resource->avatar);
                }),  
            ]),
        ]);
    }
}

Also, we should update App\Models\User class for our case:

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
    'email',
    'first_name',
    'last_name',
    'firebase_token',
];

After we create a new resource we should tell laravel to bind to our resource class rather, then to kit's one. Add given code row into extendsCoreKitBinds function in AppServiceProvider:

// Replace Core User Resource
$this->app->bind(\AttractCores\LaravelCoreKit\Http\Resources\UserResource::class, \App\Http\Resources\UserResource::class);

After that manipulations, any UserResource response in CoreKit actions will use your class rather, then core one.

Translation text

  • Your email address is not verified.
  • You should specify at least one role.
  • This role does not exist, or you are trying to use deprecated role for your access.
  • Slug field is required.
  • Slug should be unique.
  • Role name is required.
  • You can't create a role without permissions.
  • Given permission is not exists in our db.
  • Name field is required.
  • Name length should be less than 255 chars.
  • Name should be unique.
  • Is active flag is required.
  • Flag value should be boolean.