Download the PHP package ernestobaezf/ltoolkit without Composer

On this page you can find all versions of the php package ernestobaezf/ltoolkit. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package ltoolkit

Laravel Toolkit

This is a laravel package to save time by implementing a lot of common action when creating (mainly) an API oriented application following different design patterns. Here you can find the basics for:

With this package is possible to structure a project in pseudo-packages. This means you can divide the logic of your project in a directory called packages and separate the business of your project in logic units. In this case the expected directory structure is:

project |+ app |- ... |- packages |- Package1 |+ Models |+ Http |+ Repositories |+ Resources |- Package1ServiceProvider.php |- Package2 |+ Models |+ Http |+ Repositories |+ Resources |- Package2ServiceProvider.php

And then configure the namespace for this directory in the composer.json file as below:

"autoload": {
        "psr-4": {
            ...
            "Packages\\": "packages"
        },

Of course, is not mandatory to structure the project as explained. You can do use it as you want.

Installation

Install via composer:

composer require ernestobaezf/ltoolkit

Then in your config/app.php add the following provider to the list:

'providers' => [
     ...
     LToolkit\ServiceProvider::class,
]

Service provider

The service provider is the entry point to connect packages with Laravel main project. Since by using this package you can divide your project in packages, then there are many common operations that can be repeated, so we have created BaseServiceProvider. This way each time is required to create a new package is going to be as easy as extends this class in the new package. But there are some things you would like to customize beyond the default implementation and that is what are going to explain next.

Repository pattern

The repository pattern is to access the model layer (whether local or remote).

This package provide a finder that allows you to get the corresponding Repository for a given model by following conventions we are going to define in this section. In the implementation of this pattern there are different parts involved:

UnitOfWork

Because, in theory, the repository is just a collection of entities then is necessary to have a unit to actually modify the database. Why is this? Imagine the following cases:

but, what if you want to avoid send a request to the database each time, for performance reasons, and send only once instead?

in this case the right thing is to have an atomic operation, which means to create the transaction related to the user only if the user was created (and didn't fail in the process).

In this two cases we need what is called the Unit of Work, which actually controls the operations to persist the data. Normally this part of the logic is transparent since no additional configuration is required to use it because is configured by default to auto-commit the changes and persist the data.

But to tackle the previous cases we need to bind a different instance of the unit of work, for instance:

$this->app->when(SampleAPIController::class)
            ->needs(UnitOfWorkInterface::class)
            ->give(function() {
                return $this->app->make(UnitOfWork::class, ["autoCommit" => false]);
            });

So the previous cases will be like:

Notice two things from here:

  1. We advise to never user DB::beginTransaction, DB::commit nor DB::rollback directly in code. Always use the Unit of Work instead.

  2. Manually commit or rollback by using the Unit of Work only if you are in presence of one of the cases exposed before or a variation that requires to control the data persistence.

Repository structure

The Unit of Work as the entry point to use the repository has a method UnitOfWork::getRepository($entityClass). This is based in a repository finder that discovers repositories related to an entity following the rules below:

  1. Returns the repository mapped to the entity in the config.LToolkit.repository_map. If there is a mapping declared and the associated repository does not exist then an exception is thrown.

  2. If there is not mapping like in the first step then it returns the associated repository (following the naming convention) placed in the repository directory associated to the entity namespace. If an associated repository is not found then a base GenericRepository is returned.

  3. If there is no mapping specified then the repository returned is the result of following the step 2 with the default mapping (Packages\<package_name>\Models => Packages\<package_name>\Repositories)

Naming convention: The repositories are places always inside the directory Repositories and the name starts with the corresponding model name and ends by Repository. For instance: SampleRepository for the name of the repository corresponding to Package\<package_name>\Models\Sample.

$this->app->singleton("Http\Client\HttpClient", Http\Adapter\Guzzle6\Client::class);

API controllers

There is a BaseAPIResourceController which implements APIResourceControllerInterface that defines the minimum and a common set of methods (index, show, store, update, destroy) to handle CRUD. This BaseAPIResourceController has injected a Unit of Work that allows to get the repositories according the requiring entity class, together with a ValidatorResolver and a CriteriaResolverInterface as parameter in the constructor. CriteriaResolver has as a default param, a RequestCriteria, to filter the results with a criteria defined in l5-repository package. This association can be adapted to different needs in the bindings (see Service Provider section) or extending the controller and passing a new CriteriaResolver with the customized array of criteria for the specific case.

To access remote data, the idea is to follow the same structure and pattern as the BaseAPIResourceController by using the RemoteRepository to access remote data. So is needed to:

  1. Declare a local model for the remote model

  2. Create the repository for that model extending the RemoteRepository base class

  3. Define the methods corresponding those exposed by the micro-service in the repository created in step 2.

By doing this we are following the same logic pipeline and a lot of code can be reused. By saying this we remark that is not necessary and we strongly discourage the use of Guzzle or any other http client directly in code; use the repository instead.

Validation

Every action from Controllers that extend BaseAPIController search for the matching Validator by following the conventions below:

  1. Inside the directory Http, together with directories Controllers and Middleware, create the directory Validators

  2. Inside Http/Validators create a directory with the name of the controller without the postfix "Controller"

  3. Inside create the class <action_name>Validator (for instance StoreValidator) and define the rules for the given action.

If you want to declare the validators yourself and not follow the convention, you can do it as below:

class SampleController extends BaseAPIController
{
    public function __construct(UnitOfWorkInterface $unitOfWork)
    {
        parent::__construct(
            $unitOfWork, app(
                ValidatorResolverInterface::class, [
                    "className" => static::class,
                    "validations" => [
                        "action" => new ActionValidator()
                    ]
                ]
            )
        );
    }

    ...
}

Logs

The activity on every action from Controllers that extend BaseAPIController is logged. This is done by using a middleware. Also is recommended to use CustomLogFormatter to get an easy to parse log records (see an example configuration below).

config/logging.php

'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'formatter' => \LToolkit\Formatters\CustomLogFormatter::class,
            'formatter_with' => [
                "format" => "[%datetime%] %channel%.%level_name% %context% %extra% %message%\n",
                "dateFormat" => null,
                "allowInlineLineBreaks" => true,
                "ignoreEmptyContextAndExtra" => false
            ],
            'level' => 'debug',
            'days' => 14,
        ],

There are 2 ways to log the activity in a function:

Pro-actively:

Class SomeLoggableClass { use TLogAction;

    ...

    public function all($columns = ['*']): Collection
    {
        return $this->evaluate(
            function () use ($columns) {
                return $this->getInternalRepository()->all($columns);
            }, __FUNCTION__
        );
    }
}

Then once the method is called (new SomeClass())->all() it will automatically log the method activity when it starts the execution and when is finished.

To enable these logs is required to set the environment variable LOG_ACTIONS=true.

Re-actively:

/**

  • @method logAll($columns = ['']): Collection / Class SomeLoggableClass implements LoggableInterface { use TLoggable;
    ...

    public function all($columns = ['*']): Collection
    {
        return $this->getInternalRepository()->all($columns);
    }
}

Then the method can be executed:

  1. (new SomeClass())->log(all, ['column1', 'column2']) or

  2. (new SomeClass())->logAll(['column1', 'column2'])

It will automatically log the method activity when it starts the execution and when is finished.

Is possible to configure the level with the environment variable LOGGABLE_LOG_LEVEL=<log_level>, by default is debug which means that to see these logs the level must be configured to debug on config/logging.php.

Localization

Is possible to get the api messages in english and spanish by using the route middleware Localization and sending to the backend the current language in the headers 'X-localization'. If no language is specified then the one from user preference (if specified) or english is used as default.

To set the middleware, in the file app\Http\Kernel.php set:

protected $routeMiddleware = [
    ...
    'l18n' => \LToolkit\Http\Middleware\Localization::class,
];

Use case

To create a simple API is as easy as:

  1. Create the model in Model directory

  2. Create a Controller, extend the class BaseAPIResourceController and specify the model as follow:

    class SampleAPIController extends BaseAPIResourceController { protected function getEntity(): string { return SampleModel::class; } }

  3. If you want to add validations. Create the validations as explained in section Validations

  4. Declare your routes for the api:

Route::prefix("api/v1/") ->namespace($this->getControllersNamespace()) ->group([middleware => 'api'], function () { Route::resource('sample', 'SampleAPIController') });

And that's it. You have and api with a fully functional CRUD.

With th implementation above is possible to:

  1. Get a paginated list of elements only by passing the parameter 'limit'. If this parameter is not sent then the default result is the complete set of elements.

GET http://{{domain}}/api/v1/sample?limit=20&page=1

  1. Filter elements by fields

GET http://{{domain}}/api/v1/sample?search=element1&searchFields=name:like

  1. Get relations and its fields for the list of elements.

GET http://{{domain}}/api/v1/users?search=element1&searchFields=name:like&with=roles:id

  1. Get information from entities related the one being requested

GET http://{{domain}}/api/v1/sample/?with=:<fields,...>;:<fields,...>

Example
GET http://{{domain}}/api/v1/users/20?with=roles:id,name 

All versions of ltoolkit with dependencies

PHP Build Version
Package Version
Requires php Version ^7.1
psr/http-client Version ^1.0
prettus/l5-repository Version ^2.6
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package ernestobaezf/ltoolkit contains the following files

Loading the files please wait ....