Custom laravel migration command "[Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable" - laravel

I'm trying to create a custom laravel (5.2) migration command that basically works the same as migrate:status except it just lists the pending migrations instead of all the migrations.
To do this i've very simply copied the migrate:status into another class within my app/console directory and adjusted the code to suit my needs. However whenever I try to run it I get an error:
[Illuminate\Contracts\Container\BindingResolutionException]
Target [Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable while building [App\Console\Commands\PendingMigrations, Illuminate\Database\Migrations\Migrator].
The contents of the class itself and the fire() method doesn't seem to matter as it doesn't get that far, it fails within the __construct() method.
<?php namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Database\Migrations\Migrator;
class PendingMigrations extends Command
{
/**
* The console command name.
*
* #var string
*/
protected $name = 'migrate:pending';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Shows a list of pending migrations';
/**
* The migrator instance.
*
* #var \Illuminate\Database\Migrations\Migrator
*/
protected $migrator;
/**
* Create a new migration rollback command instance.
*
* #param \Illuminate\Database\Migrations\Migrator $migrator
* #return \Illuminate\Database\Console\Migrations\StatusCommand
*/
public function __construct(Migrator $migrator)
{
parent::__construct();
$this->migrator = $migrator;
}
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
}
}
The reason for it is likely to be something to do with the IoC container and the order with which things are loaded, but I don't know enough about the inner workings of Laravel to figure out any more than that.
It surely must be possible?
I am currently stuck on 5.2, so i'm not sure if this problem exists in more recent versions.
The only thing i've attempted so far is added the migration service provider to the top of the list in config/app.php however it didn't seem to have an affect and it was just a random guess anyway.
providers' => [
Illuminate\Database\MigrationServiceProvider::class,`
]

I got around this using:
$this->migrator = app('migrator');
but it is not necessarily the best way to do this

The Migrator instance is not bound to the class name in the IoC container, it is bound to the migrator alias.
From Illuminate\Database\MigrationServiceProvider:
/**
* Register the migrator service.
*
* #return void
*/
protected function registerMigrator()
{
// The migrator is responsible for actually running and rollback the migration
// files in the application. We'll pass in our database connection resolver
// so the migrator can resolve any of these connections when it needs to.
$this->app->singleton('migrator', function ($app) {
$repository = $app['migration.repository'];
return new Migrator($repository, $app['db'], $app['files']);
});
}
Since the class name is not bound in the IoC container, when Laravel resolves your command and attempts to resolve the Migrator dependency, it attempts to build a new one from scratch and fails because the Illuminate\Database\Migrations\MigrationRepositoryInterface is also not bound in the IoC container (hence the error you're receiving).
Since Laravel can't figure this out itself, you need to either register the binding for the Migrator class name, or you need to register the binding for your command. Laravel itself registers all the bindings for the commands in the Illuminate\Foundation\Providers\ArtisanServiceProvider. An example of the command.migrate binding:
/**
* Register the command.
*
* #return void
*/
protected function registerMigrateCommand()
{
$this->app->singleton('command.migrate', function ($app) {
return new MigrateCommand($app['migrator']);
});
}
So, in your AppServiceProvider, or another service provider you setup, you can add one of the following:
Register the command in the IoC:
$this->app->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
Or, register the Migrator class name in the IoC:
$this->app->singleton(\Illuminate\Database\Migrations\Migrator::class, function ($app) {
return $app['migrator'];
});

As I don't want to register the migrator everywhere in the app, but I still want to extend the MigrateCommand itself, I came up with this approach to maintain my app as it is:
public function __construct()
{
app()->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
parent::__construct(app('migrator'));
}

Related

Laravel run code when new user registrated with default registration code

Im running a laravel 6.9 application with default authentication/registration.
I want to maintain the default registration process but i want to run a curl command if a user has registered.
Is it possible to hook into the default registration process and extend it with extra code?
Observer is good point in code where you can, well, observe if user is just registered but good place to put additional code after user has been registered is event/listener group. There is already Registered event set in EventServiceProvider so you would need to put additional listener beside one already set there (for sending email to newly registered user if opted). To have all sorted next steps should be followed (disclaimer: I am taking that you use all default auth code so far):
First copy registered(Request $request, $user) method from Illuminate\Foundation\Auth\RegistersUsers.php trait to default App\Http\Controllers\Auth\RegisterController
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
//
}
So you would override that piece of default code which is meant to stay intact (as should every code from vendor directory).
Then, you would need to create listener. In App\Providers\EventServiceProvider::listen array, add one more class into value array so it should looks like
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
\App\Listeners\FooBarBaz::class,
],
];
Don't bother for not having created class already, next artisan command will do that for you:
php artisan event:generate
Now, in \App\Listeners\FooBarBaz::class you can make your custom code related to new user:
namespace App\Listeners;
use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class FooBarBaz
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param Registered $event
* #return void
*/
public function handle(Registered $event)
{
// $event->user is accessible here
//
// this method should return void, it is just executed
}
}
Now, back to registered method of RegisterController. Here you would need to initiate event:
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
event(new \Illuminate\Auth\Events\Registered($user));
}
And you are done.
I wanted to show you use of already lot of prebuilt code although Observer is also good place. But also for calling event; I wouldn't put more code than this event(new \Illuminate\Auth\Events\Registered($user)); line into UserObserver::created(User $user). Whole part with event/listener is very good and decoupled now. Of course, you can make even custom event not using default Illuminate's one, just set that new key => value into EventServiceProvider.
Events
Observers

Understand Laravel 5.3 Events

I create event using command php artisan event:make EventTest and that generates this class:
class EventTest
{
use InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
Official documentation for Laravel 5.3 Events however looks different https://laravel.com/docs/5.3/events#defining-events, event that it uses as example looks like this:
class OrderShipped extends Event
{
use SerializesModels;
public $order;
/**
* Create a new event instance.
*
* #param Order $order
* #return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
}
As you can see it extends Event, my Event created by artisan does not extend Event, also my class uses InteractsWithSockets while class on laravel documentation does not. Why this difference, what does it mean? Is Laravel's 5.3 documentation already out of date and not updated to reflect latest changes?
It appears you're correct. It looks like this particular part of the documentation was not updated with the 5.3 release.
To answer your question more completely, Laravel 5.3 no longer uses the abstract Event class you were seeing referenced in the 5.2 documentation.
The abstraction itself was unnecessary, and so it was removed.
As for the InteractsWithSockets trait you're seeing, that was added for a more seamless integration with Websockets. If your application isn't using Websockets (see: Socket.io), then you don't have to leverage these traits and the use statement may be removed safely.
Anyone may edit the documentation, so you may want to consider putting in a pull request with the docs here.

How to swap Laravel implementation with Behat and Mockery

I have a Behat FeatureContext for which I want to swap a Laravel implementation of a given class with a mocked one.
so I have this method, with a #beforeSuite annotation
/**
* #static
* #beforeSuite
*/
public static function mockData()
{
$unitTesting = true;
$testEnvironment = 'acceptance';
$app = require_once __DIR__.'/../../../bootstrap/start.php';
$app->boot();
$fakeDataRetriever = m::mock('My\Data\Api\Retriever');
$fakeData = [
'fake_name' => 'fake_value'
];
$fakeDataRetriever->shouldReceive('getData')->andReturn($fakeData);
$app->instance('My\Data\Api\Retriever', $fakeDataRetriever);
}
So I see the Laravel app and the fake data being swapped, but when I run Behat, it is being ignored, meaning Laravel is using the actual implementation instead of the fake one.
I'm using Laravel 4.2
Does someone know a way to swap Laravel implementations when running Behat?
The reason I need this is because the data is coming from remote API and I want the test to run without hitting the API.
I'm not too familiar with Behat besides what I just read in a quick tutorial to see if I can help found here... http://code.tutsplus.com/tutorials/laravel-bdd-and-you-lets-get-started--cms-22155
It looks like you are creating a new instance of Laravel, setting an instance implementation inside of it, then you are not doing anything with the Laravel instance. What's likely happening next is the testing environment is then going ahead and using its own instance of Laravel to run the tests on.
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use PHPUnit_Framework_Assert as PHPUnit;
use Symfony\Component\DomCrawler\Crawler;
use Illuminate\Foundation\Testing\ApplicationTrait;
/**
* Behat context class.
*/
class LaravelFeatureContext implements SnippetAcceptingContext
{
/**
* Responsible for providing a Laravel app instance.
*/
use ApplicationTrait;
/**
* Initializes context.
*
* Every scenario gets its own context object.
* You can also pass arbitrary arguments to the context constructor through behat.yml.
*/
public function __construct()
{
}
/**
* #BeforeScenario
*/
public function setUp()
{
if ( ! $this->app)
{
$this->refreshApplication();
}
}
/**
* Creates the application.
*
* #return \Symfony\Component\HttpKernel\HttpKernelInterface
*/
public function createApplication()
{
$unitTesting = true;
$testEnvironment = 'testing';
return require __DIR__.'/../../bootstrap/start.php';
}
/**
* #static
* #beforeSuite
*/
public function mockData()
{
$fakeDataRetriever = m::mock('My\Data\Api\Retriever');
$fakeData = [
'fake_name' => 'fake_value'
];
$fakeDataRetriever->shouldReceive('getData')->andReturn($fakeData);
$this->app->instance('My\Data\Api\Retriever', $fakeDataRetriever);
}
}

Laravel 5 - Queued Commands throwing spl_autoload_call() error

UPDATE - This has been narrowed down to beanstalkd, sync works
I am receiving the following error when attempting to run queued commands in my production environment:
exception 'ErrorException' with message 'unserialize(): Function spl_autoload_call() hasn't defined the class it was called for'
in /home/forge/default/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:74
I have tried both the beanstalkd and database drivers, no change. For simplicity, I am using the following command:
<?php namespace App\Commands;
use App\Commands\Command;
use App\User;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldBeQueued;
class TestQueueCommand extends Command implements SelfHandling, ShouldBeQueued {
use InteractsWithQueue, SerializesModels;
/**
* #var User
*/
private $user;
/**
* Create a new command instance.
*
* #param User $user
*/
public function __construct(User $user)
{
//
$this->user = $user;
}
/**
* Execute the command.
*
* #return void
*/
public function handle()
{
\Log::info("You gave me " . $this->user->fullName());
}
}
Dispatch code:
get('queue-test', function()
{
Bus::dispatch(new TestQueueCommand(User::first()));
});
This works in my Homestead environment, fails in production (Digital Ocean, Forge). I have several beanstalkd workers and I have tried restarting them. I have also run php artisan queue:flush.
Here is the code where the error is occurring (from source):
/**
* Handle the queued job.
*
* #param \Illuminate\Contracts\Queue\Job $job
* #param array $data
* #return void
*/
public function call(Job $job, array $data)
{
$command = $this->setJobInstanceIfNecessary(
$job, unserialize($data['command'])
);
$this->dispatcher->dispatchNow($command, function($handler) use ($job)
{
$this->setJobInstanceIfNecessary($job, $handler);
});
if ( ! $job->isDeletedOrReleased())
{
$job->delete();
}
}
In the past, I also ran into a similar issue while unserializing. The problem was the default Beanstalk job size (65,535 bytes), which might not be big enough if the class being serialized contains lots of properties that need to be kept (increasing the size of the serialized string and using more than 65K for storage).
In order to solve this, try setting the size to 131,072 or even 262,144 bytes using the -z option, on the configuration file (/etc/default/beanstalkd):
BEANSTALKD_EXTRA="-z 262144"
After that, you should restart the service.
Also note that the configuration file path might be other, depending on the distribution you're using.
And since you're using Digital Ocean, you might find their documentation useful.

Laravel 4 artisan command not seeing binding

I created a command called AttachUsers. When I run the command I get
Argument 1 passed to AttachPhones::__construct() must be an instance of Acme\\Repositories\\Organizations\\OrganizationRepositoryInterface, none given, called in \/Users\/jonson\/sites\/acme\/app\/start\/artisan.php on line 5 and defined","file":"\/Users\/jonson\/sites\/acme\/app\/commands\/AttachPhones.php","line":30}}
I have bound my interface in the repositories service provider file AND it is currently woking in my controllers. My command file is as follows
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use \Acme\Repositories\Organizations\OrganizationRepositoryInterface;
class AttachUsers extends Command {
protected $organizations;
/**
* The console command name.
*
* #var string
*/
protected $name = 'acme:attach_users';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Cron command to attach users from import to organizations';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct(OrganizationRepositoryInterface $organizations)
{
$this->organizations = $organizations;
parent::__construct();
}
You're probably calling Artisan::add(new MyCommand). Any time you use the "new" keyword, the IoC container is completely bypassed, and automatic dependency injection will not work. Instead, use Artisan::add(App::make('MyCommand')) or the shorthand, Artisan::resolve('MyCommand').

Resources