/starter-framework

Starter framework for laravel and lumen to generate api fast

Primary LanguagePHPMIT LicenseMIT

starter-framework

Latest Stable Version Latest Unstable Version Total Downloads StyleCI Code Climate Scrutinizer Code Quality Build Status Analytics License

Overview

This package is suitable for Restful API oriented projects

The Restful APIs as a backend layer which provide simple unified interfaces for frontend: Web and Mobile apps.

It utilized Laravel Passport to authenticate protected resources.

It is fully utilised Repository Design pattern.

Features

  • Dingo Api Support
  • l5-Repository Support
  • Laravel Passport Support
  • ide-helper Support
  • clockwork support
  • Cors support
  • All of the above are supported for laravel & lumen

Getting started

Installation via Composer

composer require milkmeowo/starter-framework

Register the ServiceProvider

Register the starter-framework service provider by adding it to the providers array.

Laravel

'providers' => array(
    ...
    Milkmeowo\Framework\Base\Providers\LaravelServiceProvider::class,
)

Lumen

Modify the bootstrap flow (bootstrap/app.php file)

// Enable Facades
$app->withFacades();

// Enable Eloquent
$app->withEloquent();

$providers = [
    ...
    Milkmeowo\Framework\Base\Providers\LumenServiceProvider::class,
];

array_walk($providers, function ($provider) use ($app) {
    $app->register($provider);
});

Publish the configure

then copy config files into your project.you can finish this manually

cp -R vendor/milkmeowo/starter-framework/config ./config

or for Laravel you can use

php artisan vendor:publish

and change config/auth.php api guards driver to passport

 'guards' => [
        ...
        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],
    ],

Migrate and install Laravel Passport

# Create new tables for Passport
php artisan migrate

# Install encryption keys and other necessary stuff for Passport
php artisan passport:install

Packages Documents

Dingo

improve

Exception Handle Register

add src/Framework/Dingo/Providers/ExceptionHandlerServiceProvider to register the common Exception handle

  • Illuminate\Auth\Access\AuthorizationException
  • Illuminate\Auth\AuthenticationException
  • Illuminate\Database\Eloquent\ModelNotFoundException
  • Illuminate\Http\Exception\HttpResponseException
  • League\OAuth2\Server\Exception\OAuthServerException
  • Prettus\Validator\Exceptions\ValidatorException

ResponseWasMorphed Event Listener

fire src/Framework/Dingo/Listeners/AddPaginationLinksToResponse

Auth Providers

add Passport and improved Oauth2 Auth Providers

you can use with config

// file config/api.php

...
    'auth' => [
        Milkmeowo\Framework\Dingo\Auth\Providers\OAuth2::class,
        Milkmeowo\Framework\Dingo\Auth\Providers\Passport::class,
    ],
...

reference:

Dingo Api

Dingo Api Wiki documentation

dingo-api-wiki-zh.

l5-Repository

improve

artisan command

// example for generate Post Entity Repository
php artisan starter:entity Post

auto generate below files

modified:   routes/api.php
modified:   app/Providers/RepositoryServiceProvider.php

     add:   app/Api/Controllers/V1/PostsController.php
     add:   app/Models/Post.php
     add:   app/Presenters/PostPresenter.php
     add:   app/Repositories/Criteria/PostCriteria.php
     add:   app/Repositories/Eloquent/PostRepositoryEloquent.php
     add:   app/Repositories/Interfaces/PostRepository.php
     add:   app/Transformers/PostTransformer.php
     add:   app/Validators/PostValidator.php
     add:   database/migrations/2017_01_12_091412_create_posts_table.php
     add:   database/seeds/PostSeeder.php
  • Controller(index store show update destory trashedIndex trashedShow restore)
  • Models(BaseModel with ObserveEvent auto track record modify with create(update/delete)_at(by/ip))
  • Presenter
  • Transformer(league/fractal)
  • Repositories/Criteria
  • Repositories/Eloquent(BaseEloquent with ObserverEvents,Cacheable,SoftDeletes Support)
  • Repositories/Interfaces
  • Validator(prettus/laravel-validator support)
  • migrations(new blueprint methods recordStamps[create(update)_at(by/ip)] softDeletesRecordStamps[delete_at(by/ip)])
  • Seeder
  • append Route(file routes/api.php(web.php) with placeholder //:end-routes: can auto-inject Restful api)
  • bind Repository Interface and Eloquent

Routes Endpoint

files routes/api.php with placeholder //:end-routes:

<?php

$api = app('Dingo\Api\Routing\Router');

// v1 version API
// choose version add this in header    Accept:application/vnd.lumen.v1+json
$api->version('v1', [
    'namespace' => 'App\Api\Controllers\V1',
], function ($api) {
    
    /*
    |------------------------------------------------
    | User Routes
    |------------------------------------------------
    */

    // trashed listing
    $api->get('/users/trashed', ['as' => 'users.trashed.index', 'uses' => 'UsersController@trashedIndex']);

    // show trashed special resource
    $api->get('/users/trashed/{id}', ['as' => 'users.trashed.show', 'uses' => 'UsersController@trashedShow']);

    // restore
    $api->put('/users/{id}/restore', ['as' => 'users.restore', 'uses' => 'UsersController@restore']);

    // listing
    $api->get('/users', ['as' => 'users.index', 'uses' => 'UsersController@index']);

    // create
    $api->post('/users', ['as' => 'users.store', 'uses' => 'UsersController@store']);

    // show
    $api->get('/users/{id}', ['as' => 'users.show', 'uses' => 'UsersController@show']);

    // update
    $api->put('/users/{id}', ['as' => 'users.update', 'uses' => 'UsersController@update']);

    // delete
    $api->delete('/users/{id}', ['as' => 'users.destroy', 'uses' => 'UsersController@destroy']);

    //:end-routes:
});
Verb URI Action Route Name
GET /users index users.index
GET /users/trashed trashedIndex users.trashed.index
POST /users store users.store
GET /users/{id} show users.show
GET /users/trashed/{id} trashedShow users.trashed.show
PUT /users/{id} update users.update
PUT /users/{id}/restore restore users.restore
DELETE /users/{id} destroy users.destroy

ObserveEvent

Models and Repositories/Eloquent has listen below methods events with $priority = 99

  • onCreating()
  • onCreated()
  • onUpdating()
  • onUpdated()
  • onSaving()
  • onSaved()
  • onDeleting()
  • onDeleted()
  • onRestoring()
  • onRestored()

Models also has default record track event listen

fields default description
$autoRelatedUserId protect / true Indicates if the model should be auto set user_id.
$userstamps protect / true Indicates if the model should be recorded users.
$ipstamps protect / true Indicates if the model should be recorded ips.
RELATED_USER_ID const / 'user_id' The name of the "related user id" column.
CREATED_BY const / 'created_by' The name of the "created by" column.
UPDATED_BY const / 'updated_by' The name of the "updated by" column.
CREATED_IP const / 'created_ip' The name of the "created ip" column.
UPDATED_IP const / 'updated_ip' The name of the "updated ip" column.
DELETED_IP const / 'deleted_ip' The name of the "deleted ip" column.
DELETED_BY const / 'deleted_by' The name of the "deleted by" column.
  • onCreating()
    // auto set related user id
    if ($this->autoRelatedUserId && empty($this->{static::RELATED_USER_ID}) && $this->hasTableColumn(static::RELATED_USER_ID)) {
        $user_id = $this->getAuthUserId();
        if ($user_id > 0) {
            $this->{static::RELATED_USER_ID} = $user_id;
        }
    }
  • onSaving()
    // update ipstamps if true
    if ($this->ipstamps) {
        $this->updateIps();
    
    // update userstamps if true
    if ($this->userstamps) {
        $this->updateUsers();
    }
    
    protected function updateIps()
    {
        $ip = smart_get_client_ip();

        if (! $this->isDirty(static::UPDATED_IP) && $this->hasTableColumn(static::UPDATED_IP)) {
            $this->{static::UPDATED_IP} = $ip;
        }

        if (! $this->exists && ! $this->isDirty(static::CREATED_IP) && $this->hasTableColumn(static::CREATED_IP)) {
            $this->{static::CREATED_IP} = $ip;
        }
    }
    
    protected function updateUsers()
    {
        $user_id = $this->getAuthUserId();
        if (! ($user_id > 0)) {
            return;
        }

        if (! $this->isDirty(static::UPDATED_BY) && $this->hasTableColumn(static::UPDATED_BY)) {
            $this->{static::UPDATED_BY} = $user_id;
        }

        if (! $this->exists && ! $this->isDirty(static::CREATED_BY) && $this->hasTableColumn(static::CREATED_BY)) {
            $this->{static::CREATED_BY} = $user_id;
        }
    }
  • onDeleting()
    if (static::usingSoftDeletes()) {
        if ($this->hasTableColumn(static::DELETED_BY)) {
            $this->{static::DELETED_BY} = $this->getAuthUserId();
        }

        if ($this->hasTableColumn(static::DELETED_IP)) {
            $this->{static::DELETED_IP} = smart_get_client_ip();
        }

        $this->flushEventListeners();
        $this->save();
    }
  • onRestoring()
    if ($this->hasTableColumn(static::DELETED_BY)) {
        $this->{static::DELETED_BY} = null;
    }
    if ($this->hasTableColumn(static::DELETED_IP)) {
        $this->{static::DELETED_IP} = null;
    }

Repositories/Eloquent

  • Cacheable

  • SoftDeletes Support

    • withTrashed
    • withoutTrashed
    • onlyTrashed
    • restore
    • restoreWhere
    • ForceDelete
    • forceDeleteWhere

reference:

l5-repository

l5-repository Usage

prettus/laravel-validator

league/fractal

Laravel 的中大型專案架構.

Illuminate Database

improve

  • support fulltext index
  • add a set of blueprint methods
    • add ipStamps(create_ip update_ip)
    • add userStamps(create_by update_by)
    • add recordStamps(timestamps ipStamps userStamps)
    • add softDeletesStamps(delete_ip delete_by)
    • add softDeletesRecordStamps(softDeletes softDeletesStamps)
    • add dropIpStamps
    • add dropUserStamps
    • add dropRecordStamps
    • add dropSoftDeletesStamps
    • add dropSoftDeletesRecordStamps
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            
            $table->recordStamps();
            
            $table->softDeletesRecordStamps();
            
            $table->text('content')->fulltext()
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('users');
    }
}

laravel passport

Laravel

reference:

Laravel Passport

Official Documentation.

Lumen

Installed routes

Adding this service provider, will mount the following routes:

Verb Path NamedRoute Controller Action Middleware
POST /oauth/token \Laravel\Passport\Http\Controllers\AccessTokenController issueToken -
GET /oauth/tokens \Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController forUser auth
DELETE /oauth/tokens/{token_id} \Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController destroy auth
POST /oauth/token/refresh \Laravel\Passport\Http\Controllers\TransientTokenController refresh auth
GET /oauth/clients \Laravel\Passport\Http\Controllers\ClientController forUser auth
POST /oauth/clients \Laravel\Passport\Http\Controllers\ClientController store auth
PUT /oauth/clients/{client_id} \Laravel\Passport\Http\Controllers\ClientController update auth
DELETE /oauth/clients/{client_id} \Laravel\Passport\Http\Controllers\ClientController destroy auth
GET /oauth/scopes \Laravel\Passport\Http\Controllers\ScopeController all auth
GET /oauth/personal-access-tokens \Laravel\Passport\Http\Controllers\PersonalAccessTokenController forUser auth
POST /oauth/personal-access-tokens \Laravel\Passport\Http\Controllers\PersonalAccessTokenController store auth
DELETE /oauth/personal-access-tokens/{token_id} \Laravel\Passport\Http\Controllers\PersonalAccessTokenController destroy auth

Please note that some of the Laravel Passport's routes had to 'go away' because they are web-related and rely on sessions (eg. authorise pages). Lumen is an API framework so only API-related routes are present.

reference:

Lumen Passport

Ide-helper

Register service provider when APP_ENV != production

Automatic phpDoc generation for Laravel Facades

php artisan ide-helper:generate

Automatic phpDocs for models

php artisan ide-helper:models

PhpStorm Meta for Container instances

php artisan ide-helper:meta

reference:

IDE Helper Generator

Clockwork

Register service provider when APP_DUBUG = true and APP_ENV != production

reference:

Clockwork

Cors

Usage

The ServiceProvider adds a route middleware you can use, called cors. You can apply this to a route or group to add CORS support.

Route::group(['middleware' => 'cors'], function(Router $router){
    $router->get('api', 'ApiController@index');
});

If you want CORS to apply for all your routes, add it as global middleware in app/http/Kernel.php:

protected $middleware = [
    ....
    \Barryvdh\Cors\HandleCors::class
];

reference:

barryvdh/laravel-cors

License

The starter-framework is open-sourced software licensed under the MIT license.