Laravel 5 dynamically run migrations - php

so I have created my own blog package in a structure of Packages/Sitemanager/Blog I have a service provider that looks like the following:
namespace Sitemanager\Blog;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
class BlogServiceProvider extends LaravelServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot() {
$this->handleConfigs();
$this->handleMigrations();
$this->handleViews();
$this->handleRoutes();
}
/**
* Register the service provider.
*
* #return void
*/
public function register() {
// Bind any implementations.
$this->app->make('Sitemanager\Blog\Controllers\BlogController');
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides() {
return [];
}
private function handleConfigs() {
$configPath = __DIR__ . '/config/blog.php';
$this->publishes([$configPath => config_path('blog.php')]);
$this->mergeConfigFrom($configPath, 'blog');
}
private function handleTranslations() {
$this->loadTranslationsFrom(__DIR__.'/lang', 'blog');
}
private function handleViews() {
$this->loadViewsFrom(__DIR__.'/views', 'blog');
$this->publishes([__DIR__.'/views' => base_path('resources/views/vendor/blog')]);
}
private function handleMigrations() {
$this->publishes([__DIR__ . '/migrations' => base_path('database/migrations')]);
}
private function handleRoutes() {
include __DIR__.'/routes.php';
}
}
Now, what i would like to do is run the migrations dynamically if they have never been run before or within an installation process i suppose. I've seen in older documentation you could so something like this:
Artisan::call('migrate', array('--path' => 'app/migrations'));
However, this is invalid in laravel 5, how can I approach this?

Artisan::call('migrate', array('--path' => 'app/migrations'));
will work in Laravel 5, but you'll likely need to make a couple tweaks.
First, you need a use Artisan; line at the top of your file (where use Illuminate\Support\ServiceProvider... is), because of Laravel 5's namespacing. (You can alternatively do \Artisan::call - the \ is important).
You likely also need to do this:
Artisan::call('migrate', array('--path' => 'app/migrations', '--force' => true));
The --force is necessary because Laravel will, by default, prompt you for a yes/no in production, as it's a potentially destructive command. Without --force, your code will just sit there spinning its wheels (Laravel's waiting for a response from the CLI, but you're not in the CLI).
I'd encourage you to do this stuff somewhere other than the boot method of a service provider. These can be heavy calls (relying on both filesystem and database calls you don't want to make on every pageview). Consider an explicit installation console command or route instead.

After publishing the package:
php artisan vendor:publish --provider="Packages\Namespace\ServiceProvider"
You can execute the migration using:
php artisan migrate
Laravel automatically keeps track of which migrations have been executed and runs new ones accordingly.
If you want to execute the migration from outside of the CLI, for example in a route, you can do so using the Artisan facade:
Artisan::call('migrate')
You can pass optional parameters such as force and path as an array to the second argument in Artisan::call.
Further reading:
https://laravel.com/docs/5.1/artisan
https://laravel.com/docs/5.2/migrations#running-migrations

For the Laravel 7(and probably 6):
use Illuminate\Support\Facades\Artisan;
Artisan::call('migrate');
will greatly work.

Related

How to load another Console/Kernel.php in Laravel project?

I have a running Laravel application which has the default Console/Kernel.php where all my scheduled commands are defined as usual.
But now I have another Laravel application that I am trying to merge with this existing application, and I have created a folder inside the existing Laravel application and I am using a Service Provider to load all the things. This way I can keep the code of the two projects separate but at the same time all features under the same repository.
<?php
namespace SecondApp;
use Illuminate\Support\ServiceProvider;
class SecondAppServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
include __DIR__ . '/routes/web.php';
include __DIR__ . '/routes/api.php';
$this->app->register('SecondApp\App\Providers\AppServiceProvider');
$this->app->register('SecondApp\App\Providers\AuthServiceProvider');
$this->app->register('SecondApp\App\Providers\BroadcastServiceProvider');
$this->app->register('SecondApp\App\Providers\EventServiceProvider');
$this->app->register('SecondApp\App\Providers\JobServiceProvider');
$this->app->register('SecondApp\App\Providers\MailerServiceProvider');
$this->app->register('SecondApp\App\Providers\RouteServiceProvider');
$this->app->register('SecondApp\App\Providers\StorageServiceProvider');
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
$this->loadMigrationsFrom(__DIR__ . '/database/migrations/');
$this->publishes([
__DIR__ . '/config/' => config_path()
], 'second-app-config');
$this->publishes([
__DIR__ . '/resources/' => base_path('resources')
], 'second-app-resources');
}
}
This is what my service somewhat looks like. Everything else seems to work well, roues, migrations, registering service providers and so on, but this second project now also has Console/Kernel.php file. And those commands are not being called yet, obviously because laravel doesn't know about it. So I am wondering how can I tell Laravel to look at that as well? Is this possible, or will I have merge the code into one main Kernel.php?
I have the same question about Http/Kernel.php as well, but I am sure if someone can suggest how to make one work, I can make the other work as well.
To load commands, need to add them like this in the register method of service provider
$this->commands([
App\Console\Commands\CommandOne::class,
App\Console\Commands\CommandTwo::class,
]);
and so on, keep adding commands to the array.
And to schedule these commands refer to this answer -> How to schedule Artisan commands in a package?
So add something like this to the boot method of your service provider:
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
$schedule->command('some:command')->everyMinute();
});

Laravel EventListener doesn't fire

I created an event and listener in Laravel, but the listener doesn't fire. It does actually fire on my colleague's machine. That makes me think that the actual code works and that the configuration is in order.
Listener:
<?php
namespace App\Listeners\Consensus;
use App\Events\Consensus\ManualGroupChannelNotificationEvent;
use Illuminate\Support\Facades\Log;
/**
* Class ManualGroupChannelNotificationListener
* #package App\Listeners\Consensus
*/
class ManualGroupChannelNotificationListener
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param ManualGroupChannelNotificationEvent $event
* #return void
*/
public function handle(ManualGroupChannelNotificationEvent $event)
{
Log::debug('Listener');
}
}
Event:
<?php
namespace App\Events\Consensus;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Support\Facades\Log;
/**
* Class ManualGroupChannelNotificationEvent
* #package App\Events\Consensus
*/
class ManualGroupChannelNotificationEvent
{
use Dispatchable;
/*
* ExternalComment constructor.
*
* #param Comment $comment
* #param User $currentUser
*/
public function __construct()
{
Log::debug('Event');
}
}
EventServiceProvider:
protected $listen = [
'App\Events\Consensus\ManualGroupChannelNotificationEvent' => [
'App\Listeners\Consensus\ManualGroupChannelNotificationListener',
],
];
Firing the event:
event(new ManualGroupChannelNotificationEvent());
I ran all commands to clear cache etc., but still it doesn't work.
php artisan clear-compiled
php artisan config:clear
php artisan cache:clear
composer dump-autoload
php artisan queue:restart
If this code works on another machine, what else can I do to make it work on mine?
Other info:
My logging does work; the Event-message is logged.
Other, similar events do work.
First thing, try to apply chmod -R 777 storage/logs it might be just an authorisation issue.
Maybe the registering of your events failed, you can try to add this to your EventServiceProvider
/**
* Determine if events and listeners should be automatically discovered.
*
* #return bool
*/
public function shouldDiscoverEvents()
{
return true;
}
Is your second working machine the same as the first one ?
Ok. I tested your setup in Laravel 5.8 by manually creating your classes and both the event and listener are logged correctly.
I will suggest you avoid manually creating your event and listener classes. Instead, first specify them in EventServiceProvider.php as you have done:
protected $listen = [
'App\Events\Consensus\ManualGroupChannelNotificationEvent' => [
'App\Listeners\Consensus\ManualGroupChannelNotificationListener',
],
];
Then generate the necessary class files automatically using this artisan command
php artisan event:generate
You can now modify the generated classes by including the use Illuminate\Support\Facades\Log; directive line and then use the Log::debug() method call in your event constructor and listener handler methods.
Try this suggested method and see if it works. I don't think it is a case of storage access permission since the event logs successfully in your case.
I deleted the repo and cloned again, it works now.

Guzzlehttp\Exception\ConnectionException: cURL error 28 resolving timeout after 2851 milisecond

I have recently integrated Algolia with my laravel application using Laravel-Scout library. Whenever, I try to search to search any products using algolia, I get GuzzelHttp\Exception\Connection\Exception. Following is the screen output of the response. The same issue also appears when I sync my database with Algolia's server. I have doubled checked my Algolia credentials in my project and they match correctly. I am running this project in linux mint-18.04 LTS, using default laravel server (neither Apache nor Nginx) and MySQL server.
It can be solved in the following way, given that you have already added Scout to your project:
Create your own app\Scout\EngineManager.php:
<?php declare(strict_types = 1);
namespace App\Scout;
use Algolia\AlgoliaSearch\Config\SearchConfig;
use Algolia\AlgoliaSearch\SearchClient as Algolia;
use Algolia\AlgoliaSearch\Support\UserAgent;
use Laravel\Scout\EngineManager as BaseEngineManager;
use Laravel\Scout\Engines\AlgoliaEngine;
class EngineManager extends BaseEngineManager
{
/**
* Create an Algolia engine instance.
*
* #return \Laravel\Scout\Engines\AlgoliaEngine
*/
public function createAlgoliaDriver()
{
$this->ensureAlgoliaClientIsInstalled();
UserAgent::addCustomUserAgent('Laravel Scout', '7.0.0');
$config = SearchConfig::create(config('scout.algolia.id'), config('scout.algolia.secret'));
$config->setConnectTimeout(10);
$algolia = Algolia::createWithConfig($config);
return new AlgoliaEngine(
$algolia,
config('scout.soft_delete')
);
}
}
Create your own app\Scout\Searchable.php:
<?php declare(strict_types = 1);
namespace App\Scout;
use Laravel\Scout\Searchable as BaseSearchable;
trait Searchable
{
use BaseSearchable;
/**
* Get the Scout engine for the model.
*
* #return mixed
*/
public function searchableUsing()
{
return app(EngineManager::class)->engine();
}
}
Create your own app\Providers\ScoutServiceProvider.php:
<?php declare(strict_types = 1);
namespace App\Providers;
use App\Scout\EngineManager;
use Laravel\Scout\ScoutServiceProvider as BaseScoutServiceProvider;
class ScoutServiceProvider extends BaseScoutServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->singleton(EngineManager::class, function ($app) {
return new EngineManager($app);
});
parent::register();
}
}
Exclude the default scout package from the package discovery in your composer.json:
"extra": {
"laravel": {
"dont-discover": [
"laravel/scout"
]
}
},
Add your ScoutServiceProvider to the provider list in config/app.php.
Run composer dumpautoload.
Whenever adding the Searchable trait to a model, add your own trait to it instead of Scout's.
If you would like to make the timeout environment-dependent, it can be extracted to a config setting. Also please keep it in mind that the above is a bare minimum for getting the Searchable trait to work with a longer timeout, other classes may also need to be overridden for other features to work.
Algolia has default timeouts and this error pops when requests are over the default (I believe 2 seconds). You can check how long requests might be taking for you with: time host [ALGOLIA_ID].algolia.net. Unfortunately, Laravel Scout doesn't have the ability to override this and I've not found a good workaround.

Laravel: dependency injection in commands

Is dependency injection of a custom class in a command possible?
I'm trying this:
<?php
namespace vendor\package\Commands;
use Illuminate\Console\Command;
use vendor\package\Models\Log;
use vendor\package\Updates\UpdateStatistics;
class UpdatePublishmentStats extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'vendorname:updatePublishmentStats';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Updates Twitter followers & Facebook page likes';
/**
* Contact implementation
* #var vendor\package\Update\UpdateStatistics
*/
protected $stats;
/**
* Create a new command instance.
*
* #return void
*/
public function __construct(
Log $log,
UpdateStatistics $stats
) {
parent::__construct();
$this->log = $log;
$this->stats = $stats;
}
But when I try to do this:
public function handle()
{
$this->stats->updateFbStats();
}
I suddenly get Segmentation fault: 11
When I delete the use vendor\package\Updates\UpdateStatistics; part, I don't get that error.
So what am I doing wrong here? Is it not possible to use dependency injection in a command?
You can inject any service in the handle method:
Note that we are able to inject any dependencies we need into the command's handle method.
Source: https://laravel.com/docs/5.8/artisan#command-structure
According to the Command Structure section of 5.2 documentation (https://laravel.com/docs/5.2/artisan#writing-commands):
"Note that we are able to inject any dependencies we need into the command's constructor. The Laravel service container will automatically inject all dependencies type-hinted in the constructor."
So I think you're good there, as far as the capability being present and available.
As for getting it to work, for me the segfault points to something wrong with the UpdateStats class, how it's referenced in the service container, or how its being resolved from the service container.
I don't have a definitive answer, but what I would do is try another class and see if I could localize the issue to this particular class, or if the problem happens with others, and then try and debug from there.
Also, if you just can't get that to work, the app() function will resolve items from the service container when you want (although looking through the 5.2 docs I don't see it anymore, so it may be deprecated - I do see $this->app->make() however).
This may work for you if nothing else does:
public function __construct(
Log $log,
) {
parent::__construct();
$this->log = $log;
$this->stats = app(UpdateStatistics::class);
}
My guess is, however, that you will get a segfault with this as well, as it should try resolving the same class the same way. If you do, then at least the error is a little clearer, and unrelated to auto-injecting feature.
Hope that at least helps a little.
Update on the app() function
So the app() function does not appear to be documented, but I have 5.2 installed right now and the helpers.php file in Illuminate/Foundation definitely has the function:
if (! function_exists('app')) {
/**
* Get the available container instance.
*
* #param string $make
* #param array $parameters
* #return mixed|\Illuminate\Foundation\Application
*/
function app($make = null, $parameters = [])
{
if (is_null($make)) {
return Container::getInstance();
}
return Container::getInstance()->make($make, $parameters);
}
}
Unfortunately the API documentation doesn't include any of the helper functions, but the current master, 5.2, and 5.3 versions of the file on Github all have the function:
https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/helpers.php#L91
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Foundation/helpers.php#L91
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/helpers.php#L91

Create an artisan command for generating custom classes or files

What's the best way ( or maybe the way it's actually done ) of creating an artisan command for generating custom classes or files? Like php artisan make:console itself that creates a php class for our new artisan command.
From what I can think of, we have two options:
Add the template for that new file using php heredoc(or any string inside the new command's class file for that matter), which is really messy.
Put a template file somewhere, read it, replace what's necessary, and then create the new file. But I don't know where would be best to put the template file.
So is there a best-practice for handling this situation in Laravel? I googled it, but there was only articles and documentation for simple artisan command creation.
Update 04/2020: Laravel 7 comes with a way to edit the default stubs to make changes to them and have Laravel pick up those changes. If you want to make a completely different stub to publish a totally different file the process below is appropriate otherwise look at the docs at the link below.
https://laravel.com/docs/7.x/artisan#stub-customization
I know this question is a bit old but this is pretty easy if you just want to create a similar file that Laravel already does. (I wanted to create a job with some custom traits attached on creation)
So first look at the stubs Laravel comes with here on github.
Next, pick the stub of the type of class you want (I copied the job-queued stub) and paste it somewhere you can access in your app. I put mine inside App\Console\Stubs since that makes sense that commands will use the stubs.
After that, create your artisan command with php artisan make:command commandName.
Inside the command created use this file Illuminate\Console\GeneratorCommand. Now make your command extend this class instead of Command; This class is the class Laravel uses to create classes and it extends Command itself.
Inside your command create a few properties and methods as follows:
protected $name = 'make:custom-file'; The name of your command. This replaces $signature
protected $description = 'Command description.';
protected $type = 'Job'; Type of class to make
//location of your custom stub
protected function getStub()
{
return app_path().'/Console/Stubs/custom-job.stub';
}
//The root location the file should be written to
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Jobs';
}
//option flags if any see this for how it works
protected function getOptions()
{
return [];
}
A full example of how the class should look is like this:
<?php
namespace App\Console\Commands;
use Illuminate\Console\GeneratorCommand;
class CustomJob extends GeneratorCommand
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $name = 'make:custom';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Create a custom job.';
/**
* The type of class being generated.
*
* #var string
*/
protected $type = 'Job';
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path().'/Console/Stubs/custom-job.stub';
}
/**
* Get the default namespace for the class.
*
* #param string $rootNamespace
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Jobs';
}
/**
* Get the console command options.
*
* #return array
*/
protected function getOptions()
{
return [];
}
}
Once you run your custom artisan command it will write your custom stub to where you specify.
Laravel uses .stub files as templates, and replaces the tokens inside the template.
Since you mentioned the make:console command, for reference you can take a look at the following files:
vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/console.stub
(on github)
This the template for making new console commands.
vendor/laravel/framework/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php
(on github)
This is the code that is executed when you run the php artisan make:console command.
If you want to take a look at packages that have done this, as well, a good example is the generators package by Jeffrey Way at Laracasts.

Categories