Divide Laravel into components

Hello, Habr. Recently I got an interesting project in my hands, which, despite its simplicity, demanded not to use any framework. There was no talk of packages, so it was decided to use the usual Laravel components. If you need to use queues, Eloquent or a container - welcome to cat.







On the simplicity of dividing the framework into components



Starting with Laravel 4, all components are separate packages, which in theory can be made to work in any PHP project. However, some components require additional customization, which in the main framework is hidden under the hood.







Components



Container



Of all the components under consideration, illuminate/container



is the easiest to install and use.







 <?php use Illuminate\Container\Container; $container = Container::getInstance(); return $container->make(Foo::class);
      
      





However, calling a static method on the Container



class every time you use a container is not a good idea. The app()



is available in the framework, which will return the global container instance to us, however, we need to create one manually.







Create a helpers.php



file for such helpers, and add it to startup.







 "autoload": { "files": [ "src/helpers.php" ], "psr-4": { "YourApp\\": "src/" } }
      
      





Add the app()



helper to the file.







 <?php use Illuminate\Container\Container; if (! function_exists('app')) { /** * Get the available container instance. * * @param string|null $abstract * @param array $parameters * @return mixed|\Illuminate\Container\Container */ function app($abstract = null, array $parameters = []) { if (is_null($abstract)) { return Container::getInstance(); } return Container::getInstance()->make($abstract, $parameters); } }
      
      





Done. You can try to use a helper.







 <?php return app(Foo::class);
      
      





Query Builder and Eloquent



To use the illuminate/database



component, we will use Capsule\Manager



, a class designed to work with the query builder outside of Laravel.







Let's start by setting up a connection to the database. It is advisable to carry out this configuration at the startup stage of your application, immediately after connecting autoload.php



.







 <?php require_once __DIR__.'/../vendor/autoload.php'; use Illuminate\Database\Capsule\Manager; $manager = new Manager; $manager->addConnection([ 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'database', 'username' => 'root', 'password' => 'password', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', ]); //        Capsule. $manager->setAsGlobal();
      
      





You can get started with the query builder.







 <?php use Illuminate\Database\Capsule\Manager; return Manager::table('orders')->get();
      
      





What about Eloquent?







 <?php namespace YourApp\Models; use Illuminate\Database\Eloquent\Model; class Order extends Model { protected $fillable = [ 'name', ]; }
      
      





 <?php use YourApp\Models\Order; Order::first();
      
      





The situation is more complicated with migrations - on the one hand, there is a Schema Builder in the kit. On the other hand, there is no automatic mechanism for starting migrations.







 <?php use Illuminate\Database\Capsule\Manager; Manager::schema()->create('orders', function ($table) { $table->bigIncrements('id'); $table->string('name'); $table->timestamps(); });
      
      





To start, just execute the file with migration: php migration.php









Queues



Queues also have their own Capsule



, however, the analogs of queue:work



and queue:listen



must be written manually.







Let's start with the Application



class. In Laravel, this class is used as a global container instance, which, in addition to the Illuminate\Container\Container



methods, contains auxiliary methods for working with the framework (current version, storage paths, resources). However, our class will contain only one method - isDownForMaintenance



. It is necessary for the work of the worker, as it determines the state of the application at the moment.







 <?php namespace YourApp; use Illuminate\Container\Container; class Application extends Container { public function isDownForMaintenance() { return false; } }
      
      





Next, we need to register the illuminate/events



provider, and bind the Illuminate\Contracts\Events\Dispatcher



contract to the events



alias.







 <?php use YourApp\Application; use Illuminate\Contracts\Events\Dispatcher; $application = Application::getInstance(); $application->bind(Dispatcher::class, 'events');
      
      





Now you need to create the Capsule



instance, and add the connection configurations there.







Example configuration for the database







 <?php use YourApp\Application; use Illuminate\Queue\Capsule\Manager; use Illuminate\Database\Capsule\Manager as DB; use Illuminate\Database\ConnectionResolver; $container = Application::getInstance(); $queue = new Manager($container); $queue->addConnection([ 'driver' => 'database', 'table' => 'jobs', 'connection' => 'default', 'queue' => 'default', ], 'default'); // ,    Illuminate\Database\Capsule\Manager       $queue->setAsGlobal(); $connection = Capsule::schema()->getConnection(); $resolver = new ConnectionResolver(['default' => $connection]); $queue->getQueueManager()->addConnector('database', function () use ($resolver) { return new DatabaseConnector($resolver); });
      
      





Configuration example for Redis (requires illuminate/redis



)







 <?php use Illuminate\Redis\RedisManager; use Illuminate\Queue\Capsule\Manager; $container->bind('redis', function () use ($container) { return new RedisManager($container, 'predis', [ 'default' => [ 'host' => '127.0.0.1', 'password' => null, 'port' => 6379, 'database' => 0, ] ]); }); $queue = new Manager($container); $queue->addConnection([ 'driver' => 'redis', 'connection' => 'default', 'queue' => 'default', ], 'default'); $queue->setAsGlobal();
      
      





The final step in the configuration is to add an analog Exception Handler .







 <?php use Illuminate\Contracts\Debug\ExceptionHandler; class Handler implements ExceptionHandler { public function report(Exception $e) { // ,  Exception     . // :   Sentry,    . } public function render($request, Exception $e) { //   } public function renderForConsole($output, Exception $e) { //  ,     -  } public function shouldReport(\Exception $e) { //      } } app()->bind('exception.handler', function () { return new Handler; });
      
      





Queue configuration completed. Now you can start writing queue:work



.







 <?php require_once __DIR__.'/bootstrap/bootstrap.php'; use Illuminate\Queue\Worker; use Illuminate\Queue\Capsule\Manager; use Illuminate\Queue\WorkerOptions; $queueManager = Manager::getQueueManager(); $worker = new Worker($queueManager, app('events'), app('exception.handler')); $worker->daemon('default', 'default', new WorkerOptions);
      
      





Now to start the queue worker, just write php worker.php



.







If there is a need to use queue:listen



, then you need to create two separate files. One is a daemon that listens to the queue and runs a second file on every new job. The second file, in turn, acts as the executor of the task.







worker.php







 <?php require_once __DIR__.'/bootstrap/bootstrap.php'; use Illuminate\Queue\Listener; use Illuminate\Queue\ListenerOptions; //  ,   " ",  Laravel  artisan,        work.php. // https://github.com/laravel/framework/blob/6.x/src/Illuminate/Queue/Listener.php#L72 define('ARTISAN_BINARY', 'work.php'); $worker = app(Listener::class, ['commandPath' => __DIR__]); $worker->setOutputHandler(function ($type, $line) { echo $line; }); $worker->listen('default', 'default', new ListenerOptions);
      
      





work.php







 <?php require_once 'bootstrap/bootstrap.php'; use Illuminate\Queue\Worker; use Illuminate\Queue\WorkerOptions; use Illuminate\Queue\Capsule\Manager; $queueManager = Manager::getQueueManager(); $worker = new Worker($queueManager, app('events'), app('exception.handler')); $worker->runNextJob('default', 'default', new WorkerOptions);
      
      





We pass to use. All methods can be viewed in the API.







 <?php use Illuminate\Queue\Capsule\Manager; Manager::push(SomeJob::class);
      
      






All Articles