How to switch interfaces in Laravel whilst developing - php

I am building a Laravel 5.2 App that will connect to a database that I don't have available to me whilst developing (it is a legacy database that I cannot install locally).
What is the cleanest way in Laravel to build my application so that I can develop using one interface, and then switch over to another when I push my project to the live environment?
In the past, I've had 2 classes, both of which have the same functions, and then in a configuration file, I would say which class it uses. Just wandering if there is a better way using Laravel? Or if there is a way of doing this already baked in?

One way I can think of achieving this is to switch the instance of the dependency injection when you're on local and production environment.
class ControllerExample
{
public function index( SomeInterface $data )
{
//...
}
}
So in this example the controller index method needs SomeInterface which would be injected automatically. For local development you can resolve this interface to your stub class and for production you can switch to the real class. You can do this by registering the binding of the interface in the app / Providers / AppServiceProvider.php in the register() method:
if ($this->app->environment() == 'local') {
$this->app->when('App\Your\ControllerExample')
->needs('App\SomeInterface')
->give('App\YourStubbedClass');
} else {
$this->app->when('App\Your\ControllerExample')
->needs('App\SomeInterface')
->give('App\YourRealClassWorkingWithTheRealDatabase');
}
What this basically does is when your env is local the dependency in your controller will be resolved with the stub class, otherwise on other envs it will be the real class.

Related

Functional testing and continuous integration with GitLab

I usually use GitLab to host my repositories and to use their CI/CD.
I made a Symfony project (PHP) with some code. I know how to configure my .gitlab-ci.yml to execute my unit tests through PHPUnit.
But I don't really understand how to execute my functional test. For example this code is just testing that the route /login is reachable and not return a 500.
class SecurityControllerTest extends WebTestCase
{
public function testLogin()
{
$client = static::createClient();
$client->request('GET', '/login');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
}
So I assume that apache/nginx is needed to interpret PHP. So do I have to make a real docker-compose with a complete LAMP stack or is there a better way?
Functional tests are executed directly in phpunit. You only need to install browser-kit and dom-crawler symfony components.
If you use an ORM, you have to declare a database image as a service and initialize your database in your CI script.

Symfony: How to call private services in Bundle Controller

I creat a bundle and I would like use best practice with it.
So all my services are private
https://symfony.com/doc/current/service_container/alias_private.html
So unless you specifically need to access a service directly from the
container via $container->get(), the best-practice is to make your
services private. In fact, the default services.yaml configuration
configures all services to be private by default.
And
https://symfony.com/blog/new-in-symfony-3-4-services-are-private-by-default
In Symfony core we've already done that and we made all services and
aliases private, except a few selected ones, that are required at
bootstrap time. In fact, bootstrapping is the last and only legitimate
use case for using the container directly.
So, should we deprecate the possibility to inject the
service_container entirely alongside with ContainerAware*? That's a
possibility that the community might consider when preparing Symfony
5.
I have an Trait or Abstract controller which have to use by App/Controller. The trait can call the private service with autowiring and the probleme is fix BUT
The best practice of Symfony is don't use only autowiring in Bundle:
https://symfony.com/doc/current/service_container/autowiring.html
Public and Reusable Bundles¶
Public bundles should explicitly configure their services and not rely
on autowiring.
So How to inject my private service in my Trait or abstract controller. Or even in the App controller without the user having to configure are services.yml.
I hope I'm clear.
Sorry for my english. I try to improve it ;-)
If you are developing a third party bundle, i.e., a bundle that will be used by other people, then you must define your services manually and also in xml. Those are the best practices.
Now, third party bundles can have public services. The article you are referencing refers to your AppBundle services, that should be private because contain your application/bussiness logic. The reason why they are private by default is because you should be using Constructor Dependency Injection to use them, so things are more easily testable.
Now, in regards to your issue, you should't be creating controllers in third party bundles: it's not good practice. Instead, use the routing to route to a service that will perform the action you need. Api Platform does that, here:
routing.xml
EntryPointAction.php

Integration testing in Symfony

I'm writing a reusable bundle and want to carry out an integration testing of this bundle with the hosting app. The problem is that Symfony doesn't call neither the DependencyInjection\Extension::load nor DependencyInjection\Configuration::getConfigTreeBuilder method of my bundle, therefore the bundle's service configuration is not loaded and the services are not constructed properly. How can I get Symfony to load everything in tests, as if it was a real request?
Here is my test:
namespace Tests\AclAgentBundle;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class IntegrationTest extends KernelTestCase
{
public function testAgentExistence()
{
$container = static::bootKernel()->getContainer();
$service = $container->get('test.Foo\Bar\Baz');
//...assertions...
}
test.Foo\Bar\Baz is an alias of a service, declared in the hosting app's config like
services:
test.Foo\Bar\Baz: '#Foo\Bar\Baz'
used to fetch the service via the container's get method in tests.
I figured out where the problem was. An older cached version of the container was used in the tests, therefore the changes in the configuration of the bundle were not reflected. The command php bin/console cache:clear --env=test helped to rebuild the cache. The methods I mentioned in the original question are not supposed to be called when booting the kernel (at least when the cache already exists).

Laravel Binding Service Provider Concept

Im currently learning how laravel framework builded and came to service provider section. in class DatabaseServiceProvider which extends ServiceProvider class i see this line of code :
$this->app->singleton('db.factory', function ($app)
{
return new ConnectionFactory($app);
});
im confuse what is app in $this->app, whether app is an object from application class instance and if it does when it is instantiated so it can be used from DatabaseServiceProvider class since i dont see its instantiation process. thnks before for answering this newbie question.
In Laravel, the app variable is a service container. Think of it like a heart of the framework. Everything you use from Facades to DB connections is stored in there.
Moreover, you can store your own objects in the container by using the method you specified ($this->app->singleton()). There are actualy many more ways to interact with the container. The best way to research this is to look into Laravel's documentation (Laravel Service Container)
The app object is being instantiated on Laravel bootstrap so you can't see it in the service provider. Though I was curious enough to dig deep into the framework to find it. The class itself is located in Illuminate\Foundation\Application and is instantiated in bootstrap/app.php.

Symfony console application: dependency injection [duplicate]

This question already has an answer here:
How to use DependencyInjection from symfony in stand alone application with commands?
(1 answer)
Closed last year.
A Symfony novice here. After reading some of the Symfony documentation and some answers here at SO, I am now almost completely confused.
I am trying to use the console application component and create a small db-aware console application.
Many people state that in order to use Symfony's DI features it would be enough to inherit my command class not from Symfony\Component\Console\Command\Command but from ContainerAwareCommand.
However when I try this I get a Method Not Found error on an application::getKernel() call.
I have a feeling that DI features are in fact not available in a console application based on the console component. Is there another kind of Symfony console application, for example, based on the full-blown framework?
I very much like the simple framework provided by the console component Symfony\Component\Console\Application. But the question is then - what to do for dependency injection and DBAL? All examples that I find seem to refer to the full Symfony framework and get me just all the more stuck.
Just a quick update on my progress if anybody stumbles upon the same problems.
I incorporated into my project the PHP-DI dependency injection framework, which seems to be working fairly well with no configuration (so far) - it figures out quite a lot by reflection, actually.
The same way, Doctrine\DBAL is included as a standalone library (I opted against the O/RM part of it, as it is really a tiny project and I'm on a much firmer ground with SQL than anything else) and the connection is simply returned by a connection provider which is injected wherever needed by the DI.
One thing I couldn't figure out is how to have the command classes instantiated by the DI library without my help, so I actually had to inject the container itself into my overridden application class and override the getDefaultCommands() where I then pull the instances out of the container manually. Not ideal but will have to do for now.
If your command extends ContainerAwareCommand
...
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
...
class MyCommand extends ContainerAwareCommand
{
The DI container is available with the getContainer() method. (like in a standard controller), ex:
$this->validator = $this->getContainer()->get('validator');
I don't know if your question is still relevant, but I have an answer as I stumbled across the same problem here.
You just have to create the kernel yourself and give it to the \Symfony\Bundle\FrameworkBundle\Console\Application that extends the basic \Symfony\Component\Console\Application.
<?php
// CronRun.php
require __DIR__.'/../../../../vendor/autoload.php';
require_once __DIR__.'/../../../../app/AppKernel.php';
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$application = new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
$application->add(new \KingdomHall\TaskBundle\Command\CronCommand());
$input = new \Symfony\Component\Console\Input\StringInput('k:c:r');
$application->run($input);
You could use a solution I just pushed it to packagist.org. Includes full working symfony/dependency-injection. You're welcome to give it a shot. use composer to create your own project composer create-project coral-media/crune project_dir or just clone the repository.
https://packagist.org/packages/coral-media/crune
You only need to install DBAL dependencies (I don't suggest ORM if you don't really need it). Configure connection parameters in .env and just define a service to handle connection. That service can be injected in your Commands using public setMyService($myService) method with #required annotation. Also you could create a Connection class and bind is as parameter in your command constructor.The crune boilerplate also supports autowire and autoconfiguring features.

Categories