Extending the class with an interface AkeneoPimClientInterface? - php

How to extend generated command in Symfony with use Akeneo\Pim\AkeneoPimClientInterface ?
I have generated a command using php app/console generate:command and I got this class:
class AppTriggerBuildCommand extends ContainerAwareCommand
Then developed it to the point when I need all the categories from the API. Seamlessly it is really an easy question, how can I use AkeneoPimClientInterface in the command.
I want to use it something like this.
$categories = $this->apiClient->getCategoryApi()->all();
And the apiClient in here comes inside the _contruct metod
public function __construct(AkeneoPimClient $apiClient, AkeneoLocaleMapper $mapper) {
$this->apiClient = $apiClient;
$this->mapper = $mapper;
}
And in use
use Akeneo\Pim\AkeneoPimClientInterface as AkeneoPimClient;
But when I tried to put it inside the _construct method in a command it want to use the parent _construct and it just can't see the generated command.
Could anyone help me ?
php app/console trigger build -vvv
[Symfony\Component\Console\Exception\CommandNotFoundException]
Command "trigger" is not defined.
Exception trace:
() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:526
Symfony\Component\Console\Application->find() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:94
Symfony\Bundle\FrameworkBundle\Console\Application->find() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:190
Symfony\Component\Console\Application->doRun() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:84
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:117
Symfony\Component\Console\Application->run() at /var/www/html/iclei/app/console:27

Since you extend the ContainerAwareCommand you have access to Symfony's Service Container to fetch the client like this (you might have to replace the fully qualified class name with a service id, I'm not 100% sure):
$this->container->get('Akeneo\Pim\AkeneoPimClientInterface');
If you want to use the constructor (which I encourage you to do), you are almost there. You just have to also call the parent constructor as well:
public function __construct(AkeneoPimClient $apiClient, AkeneoLocaleMapper $mapper)
{
parent::__construct();
$this->apiClient = $apiClient;
$this->mapper = $mapper;
}
Both ways should work, but the latter allows you to move away from the ContainerAwareCommand to the more generic ContainerCommand, which will help you with Symfony 4 where services in the container will be private by default and therefore you can not just simply get them from the container like in the first version.
edit regarding the command name: You can specify the name of your command as argument to parent::__construct() and also set it via the configure() method, you need to override. In there you can just call, e.g. $this->setName('trigger-build');. Be careful not to use spaces, as Symfony will treat those as separate arguments. So trigger is the name of the command and build is the first argument you "feed" to the command.

Related

Interface name already in use when using Repository pattern

I'm adapting repository pattern in a laravel application by creating the interface of a repository for a given model under the namespace of App\Repositories\Interfaces, and then creating the implementation class under the namespace App\Repositories, after that i bind the interface to the class in RepositoryServiceProvider.
Everything was going fine at first, but when I've created repository files for Grade model and trying to do php artisan route:list, or accessing a grade/ route I get this:
Symfony\Component\Debug\Exception\FatalErrorException thrown with message "Cannot declare interface App\Repositories\Interfaces\GradeRepositoryInterface, because the name is already in use"
Ok!, seems obvious, change the interface name and your good to go!, well NO.
the same error is raised with the same new interface name.
changing the namespace does not work either!.
I was running composer dump-autolod after changes.
Interface declaration:
use App\Models\Grade;
use App\Repositories\Interfaces\BaseRepository;
interface GradeRepositoryInterface extends BaseRepository
{
public function get(int $id) : Grade;
public function edit(int $id, array $data);
public function delete(int $id) : bool;
}
I highly doubt that symfony, or laravel have GradeRepositoryInterface interface.
Any suggestions? (Hopefully solutions)
Changed the name like GradesRepositroyInterface, and composer dump-autoload again

Unable to deeply extend Laravel Artisan Command with input passed down

Attempting to create a base command, and then extend it. Input provided to the extending command doesn't make it down the tree.
BaseApiClassBuilder.php
use Illuminate\Console\Command;
class BaseApiClassBuilder extends Command
{
// rest of class follows...
}
MakeApiCollection.php
class MakeApiCollection extends BaseApiClassBuilder
{
protected $signature = 'make:apicollection {name} {--namespace=}';
protected $description = 'Make an API Collection/Resource';
// guts of class...
}
Console Command I am running
artisan make:apicollection testApiCollection
Console Error Recieved:
The command defined in "App\Console\Commands\BaseApiClassMaker" cannot
have an empty name.
Similar question to Can you extend Command classes in Laravel but that one is a bit out of date and less specific, and also unanswered.
The error only occurs when I extend my base command, instead of 'command'.
I've determined that the validation isn't called in the constructor, it's actually quite tricky to figure out where the input is being validated, and why it isn't making it all the way down.
No doubt I'm doing something goofy and there's an easy solution... but I can't find it!
I anyone can help me understand this better, comments/answers/feedback very gratefully received. If I'm approaching this completely the wrong way, let me know too. I did notice I could have extended GeneratorCommand instead which has many of the helpers I need, but doesn't seem to solve this problem.
By making the parent class abstract you don't need to define the signature
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
abstract class BaseCommand extends Command
{
...
}
Later in child class you set the signature as needed
class ChildCommand extends BaseCommand
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'make:apicollection {specific-name} {--namespace=}';

Injecting Artisan into Service class

I am trying to inject Artisan into a service so I can avoid using the facade.
Looking at the facade class reference I can see that the class I should be injecting is:
Illuminate\Console\Application
So I would assume that doing this:
<?php
namespace App\Service;
use Illuminate\Console\Application;
class DummyDataService
{
/**
* #var Application
*/
private $application;
public function __construct(
Application $application
) {
$this->application = $application;
}
public function insertDummyData()
{
$this->application->call('db:seed', [
'--class' => 'DummyDataSeeder'
]);
}
}
...would work. However, I get the following error:
BindingResolutionException in Container.php line 824:
Unresolvable dependency resolving [Parameter #2 [ <required> $version ]] in class Illuminate\Console\Application
It works if I just call the method on the facade like so:
Artisan::call('db:seed', [
'--class' => 'DummyDataSeeder'
]);
I can't figure out what the problem is so far. Has anyone experienced any similar issues? I try to avoid facades where possible.
Thanks in advance.
You should inject Illuminate\Contracts\Console\Kernel and not Illuminate\Console\Application to achieve what you want, so your class should look like this:
<?php
namespace App\Service;
use Illuminate\Contracts\Console\Kernel;
class DummyDataService
{
private $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
public function insertDummyData()
{
$this->kernel->call('db:seed', [
'--class' => 'DummyDataSeeder'
]);
}
}
If you take a peek at the constructor for Illuminate\Console\Application you will see that it expects a $version parameter and does not provide any sort of default. Therefore, if one is not explicitly provided it will fail because of that dependency. Honestly, this seems like a bug to me. If you look at its Symphony parent class, you will see that it provides a default string of 'UNKNOWN' in its constructor. If you modify Illuminate\Console\Application to have that same default, your commands in the code should now work.
This leaves you with two options.
Just use the Artisan facade for this instance. You should be fine using constructor injection for the rest of the facades, as this affects specifically the Artisan facade.
Change the constructor in the source code to have the default. Not ideal, as all of your changes will be lost every time you update Laravel, but it is an option. You might be able to cook up some sort of service provider that injects a version into all Illuminate\Console\Application instances as well, but I'm not sure.
I am honestly unsure if there are unforeseen ramifications of adding that default into the constructor, although I would imagine they would be minimal as it must be explicitly defined everywhere it is called. I might even make this into a PR and see if Taylor comments on it or just merges it.

How to properly inject dependency into Laravel artisan command?

Basically I want to call a method on a repository Repository.php from a laravel command.
Example\Storage\Repository.php
Example\Storage\RepositoryInerface.php
Example\Storage\RepositoryServiceProvider.php
I expect Interface in the command constructor and then set it to the protected variable.
In the service provider I bind the Interface to Repository class.
Right now, in start/artisan.php I just write:
Artisan::add(new ExampleCommand(new Repository());
Can I use an interface here? What is the correct way? I am confused.
Thanks in advance.
EDIT: To clarify, it only works the way it is now, but I don't want to hardcode a concrete class while registering the artisan command.
You could use the automatic dependency injection capabiltiies of the IoC container:
Artisan::add(App::make('\Example\Commands\ExampleCommand'));
// or
Artisan::resolve('\Example\Commands\ExampleCommand');
If ExampleCommand's constructor accepts a concrete class as its parameter, then it'll be injected automatically. If it relies on the interface, you need to tell the IoC container to use a specific concrete class whenever the given interface is requested.
Concrete (ignoring namespaces for brevity):
class ExampleCommand ... {
public function __construct(Repository $repo) {
}
}
Artisan::resolve('ExampleCommand');
Interface (ignoring namespaces for brevity):
class ExampleCommand ... {
public function __construct(RepositoryInterface $repo) {
}
}
App::instance('RepositoryInterface', new Repository);
Artisan::resolve('ExampleCommand');
You may use the interface in the constructor to type hint the depended object but you have to bind the concrete class to the interface in the IoC container using something like following, so it'll work.
App::bind('Example\Storage\RepositoryInerface', 'Example\Storage\Repository');
Read more on the documentation.

Mockery shouldReceive()->once() doesn't seem to work

I'm trying to get Mockery to assert that a given method is called at least once.
My test class is:
use \Mockery as m;
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSetUriIsCalled()
{
$uri = 'http://localhost';
$httpClient = m::mock('Zend\Http\Client');
$httpClient->shouldReceive('setUri')->with($uri)->atLeast()->once();
}
}
As you can see, there's one test that (hopefully) creates an expectation that setUri will be called. Since there isn't any other code involved, I can't imagine that it could be called and yet my test passes. Can anyone explain why?
You need to call Mockery:close() to run verifications for your expectations. It also handles the cleanup of the mockery container for the next testcase.
public function tearDown()
{
parent::tearDown();
m::close();
}
To avoid having to call the close method in every test class, you can just add the TestListener to your phpunit config like so:
<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener"></listener>
</listeners>
This approach is explained in the docs.
One thing to note from the linked docs is:
Make sure Composer’s or Mockery’s autoloader is present in the bootstrap file or you will need to also define a “file” attribute pointing to the file of the above TestListener class.
Just a sidenote: If you use Laravel: the make:test --unit generates a test class that extends the original PhpUnit Testcase class and not the included Tests\Testcase, which loads the laravel app and runs the Mockery::close(). It is also the reason why in some cases your tests fail if you use Laravel specific code (like Cache, DB or Storage) in the units you're testing.
so if you need to test units with Laravel specific code, just swap out the 'extends Testcase' and there is no need to call Mockery::close() manually

Categories