Symfony 2 - non bundle library integration and location - php

I am all about the best practice of symfony 2 and I would like to integrate a php library into the project. Library is a non bundle, a simple php class with some methods.
My question just follows the following, which DOES NOT have an accepted answer. Anyway from what I read here I decided to autoload the class, but have no idea where should I locate the php file.
Maybe src/MyBundle/DependencyInjection/? I really doubt it since library has no dependency of other services I have.
Should I create a directory like src/MyBundle/Services/ or src/MyBundle/Libraries/?
What is the best practice here?

as mentioned by b.enoit.be create a service from the class.
MyBundle/Service/MyServiceClass.php
<?php
namespace MyBundle\Service;
class MyService
{
...
}
app/config/services.yml
services:
app.my_service:
class: MyBundle\Service\MyService
use it e.g. in a controller
MyBundle/Controller/DefaultController.php
<?php
namespace MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function indexAction(Request $request)
{
...
// get the service from the container
$yourService = $this->get('app.my_service');
...
}
}
?>

Related

Where to add own repository class and interfaces in Laravel?

I am attempting to add custom repsitories (contract and Eloquent) in Laravel.
I don't understand where to add them and how to bind with services.
Can any body show the best example for add own wn repository class and interfaces in Laravel?
Thanks in advance
Create a directory in your App folder.Like - App/Acme
Create a Repository File in Acme folder. App/Acme/CustomRepository.php and also import the name space on that Repository file.Like- namespace Acme;
Use your model. Like- use App\Models\User;
In you controller inject the CustomRepository Class.Like-
class CustomController extends Controller{
private $customRepo;
public function __construct(CustomRepository $customRepo)
{
$this->customRepo= $customRepo;
}
}
The way I like to structure my Laravel Code would be:
Models - App\Models\*
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as BaseModel;
class Model extends BaseModel
{
//
}
Contracts - App\Repositories\Contracts\*
<?php
namespace App\Repositories\Contracts;
interface Repository
{
// All the common methods for eloquent like - all, paginate, find, where, etc...
}
Repository - App\Repositories\Db\*
<?php
namespace App\Repositories\Db;
class ExampleRepository
{
// All the CRUD related methods here...
}
Services - App\Services\*
<?php
namespace App\Services;
class ExampleService
{
// All the logic & business related methods here...
}
This is what I like to structure my code in a laravel way.
Hope this helps!
For make usage of repository pattern (if it is that what you want to say), you have two options, one of it is to implement under a self-defined namespace (let's say App\Repositories), an interface with the methods you want to use in all your repositories, maybe AbstractRepository or something like that, this one choice is painful because of the lot of code you have to write, the other choice (what I would use), is to install the following package https://github.com/andersao/l5-repository, is really useful and already have inside a lot of methods, just follow the instructions of the readme file and you will not have any issue at all implementing this pattern, hope it helps, bests! ;)

Symfony 2 dependency injection

I'm learning symfony 2 dependency injection system. I'm trying to inject Response object in a controller.
ServiceController.php
namespace LD\LearnBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ServiceController extends Controller
{
public function indexAction(Response $response)
{
}
}
Here is the content of services.yml file (please note that it's included in app/config/config.yml
services:
ld_response:
class: Symfony\Component\HttpFoundation\Response
ld_bla:
class: LD\LearnBundle\ServiceController
arguments: ["#ld_response"]
When I try to access ServiceController I get
Class LD\LearnBundle\Controller\Response does not exist
500 Internal Server Error - ReflectionException
What I'm doing wrong?
There are 2 things wrong here:
1: "Class LD\LearnBundle\Controller\Response does not exist"
The class doesn't exist. You used Response without importing the namespace, so the error message is quite explicit here.
2: You shouldn't inject the response. It doesn't make any sense at all. The response is not a service, it's a value that should be passed down through method parameters.
Here's the fix:
namespace LD\LearnBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response; // was missing
class ServiceController extends Controller
{
public function indexAction()
{
return new Response(); // The Controller is responsible of creating the Response.
}
}
Generally, Class <current-namespace>\class does not exist errors hint to a missing use statement.
May I add that:
you shouldn't declare your services in the app/config/config.yml file (create a specific services.yml file. Even better: create it in a bundle)
you shouldn't inject the Response object: it is the responsibility of the controller to create it

Symfony 2 sharing data between controllers

I have started to create a project using Symfony 2. I need to share data between all controllers.
I have added a base controller which extends symfony\controller and each of my controllers extends this base controller
class BaseController extends Controller
class HomeController extends BaseController
This base controller will be used for things like assigning global twig variables ( I know I can do this in the config but some of the variables will be gotten from other config files and database ).
So I thought I could reference container since Controller is container aware, however it isn't at the point I am using the functions (from constructor).
public function __construct ()
I have seen people mention passing the container in as a parameter and mention services but I have had a look and cannot figure it out. All I want to achieve is this:
public function __construct (Container $container) {
$container->get('twig').addGlobal('foo');
}
This is a common stumbling block to Symfony 2 newbies. The controller/container question has been asked hundreds of time before so you are not alone(hint).
Why doesn't your controller constructor code work?
Start by looking under vendor/symfony...FrameworkBundle/Controller/Controller.php. Hmm. No constructor there so where the heck is the container coming from? We see that Controller extends ContainerAware. That seems promising. We look at ContainerAware (the namespace helps to find where the file is) and once again, no constructor. There is however a setContainer method so we can assume that the container is injected into the controller after the constructor is called. Quite common in a dependency injection based framework.
So now we know why the constructor code fails. The container has not yet been injected. Stupid design right? Time for a different framework? Not really. Let's face it, having to have all your controllers extend a base controller just to get some twig variables set is not really the best design.
The Symfony way to execute code before the controller action is executed is to make a controller event listener. It will look something like this:
namespace Cerad\Bundle\CoreBundle\EventListener;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ModelEventListener extends ContainerAware implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(KernelEvents::CONTROLLER => array(
array('doTwig', 0), // 0 is just the priority
));
}
public function doTwig(FilterControllerEvent $event)
{
// Ignore sub requests
if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) return;
$this->container->get('twig')->addGlobal('foo');
}
}
// This goes in services.yml
parameters:
cerad_core__model_event_listener__class:
Cerad\Bundle\CoreBundle\EventListener\ModelEventListener
services:
cerad_core__model_event_listener:
class: '%cerad_core__model_event_listener__class%'
calls:
- [setContainer, ['#service_container']]
tags:
- { name: kernel.event_subscriber }
So now we have the desired functionality without the need for a base controller class.
Notice also that the controller can be accessed through the event. Since the controller has been created but the action method not yet called, you could call controller methods or inject data directly into the controller. This is seldom needed. In most cases, you would add additional information to the request object which then gets injected into the controller's action method.
It's really a nice design once you get comfortable with listeners and services.
Please read carefully that question - Symfony2 passing data between bundles & controllers, try to use code included in it.
You can use service to solve your problem, for example.
If you look at the Controller class you'll se the following:
class Controller extends ContainerAware
This means you can retrieve twig from the container as simple as this:
$twig = $this->get('twig');
But I would recommend you to use custom twig extension in your case.

Symfony2 File Found Class Was Not In It

This is my first question, besides I'm not english-native speaker, so sorry in advance for newbie mistakes...
I'm starting with Symfony2, and I've been facing an autoload problem for a couple of days, i'm getting crazy..
I'm just trying to use a PHP class inside my DefaultController of my AppBundle. I've read the way of doing this is by creating a service in my config.yml and giving a namespace to that class that matches.
Symfony tells me that it does found the file but the class is not in it, the exact error is:
The autoloader expected class "Priceget\CollectorBundle\Crawler\Amazon" to be defined in file "/srv/www/lol.com/public_html/priceget/symfony/src/Priceget/CollectorBundle/Crawler/Amazon.php". The file was found but the class was not in it, the class name or namespace probably has a typo.
And my class is just this:
<?php
namespace Priceget\CollectorBundle\Crawler\Amazon;
use Symfony\Component\HttpFoundation\Response;
class Amazon
{
public function getAll()
{
return new Response('l0l');
}
}
In my DefaultController I'm calling it like that:
<?php
namespace Priceget\CollectorBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Guzzle\Http\Client;
use Symfony\Component\DomCrawler\Crawler;
use Priceget\CollectorBundle\Crawler\Amazon;
class DefaultController extends Controller
{
public function indexAction()
{
$amazon = $this->get('amazon.crawler');
}
}
And my config.yml piece:
services:
amazon.crawler:
class: Priceget\CollectorBundle\Crawler\Amazon
I've already tried to:
Empty cache
Restart apache
Extend the class to Controller? :-Z
Thank you so much in advance.
Your namespace is wrong, rename it:
from: namespace Priceget\CollectorBundle\Crawler\Amazon;
to: namespace Priceget\CollectorBundle\Crawler;
This error also occurs if you do not put <?php in the beginning of the file.
In addition to what's said by Igor, you obviously have to change the FQN class name in the service declaration (YML) if you want it to work.
This can be a bit misleading, it also happens if you don't extend your class correctly. In my instance I tried to extend a repository with an incorrect FQN:
class FilesRepository extends Doctrine\ORM\EntityRepository
should have been:
class FilesRepository extends \Doctrine\ORM\EntityRepository
Notice the missing backslash (\).

How to use Namespaced Sessions in Symfony2

I am trying to use symfony2 sessions.I do this
$session = $this->getRequest()->getSession();
$session->set('token','value');
This works. But i want to use namespace in session. Documentation says
class NamespacedAttributeBag
provides that feature but i cannot figure out how to implement it
Just open your config.yml and after imports add:
parameters:
session.attribute_bag.class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag
It looks like this:
imports:
- { resource: parameters.yml }
- { resource: security.yml }
parameters:
session.attribute_bag.class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag
framework:
# ...
You should redefine session service and also define service for your attribute bag (if you'll check default implementation of session.attribute_bag you'll see that this service has only class attribute).
And inject your new service to redefined session service into there
services:
session:
class: Symfony\Component\HttpFoundation\Session\Session
arguments:
- #session.storage
- #your.session.attribute_bag #service id is defined below
- #session.flash_bag
your.session.attribute_bag:
class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag
Because it's also possible to use the HTTPFoundation Component outside of Symfony2, the way to implement NamespacedUserBags is as follows:
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
$session = new Session();
//first bag
$myAttributeBag = new NamespacedAttributeBag('<your_storage_key_1>');
$myAttributeBag->setName('<your_tag_name_1>');
$session->registerBag($myAttributeBag);
//second bag
$myAttributeBag = new NamespacedAttributeBag('<your_storage_key_2>');
$myAttributeBag->setName('<your_tag_name_2>');
$session->registerBag($myAttributeBag);
$session->start();
Register as many bags as you want, but make sure to do this before you start the session. Now you can switch between bags using getBag():
$activeBag = $session->getBag('<your_tag_name>');
and access the namespaced bag with the typical methods :
$activeBag->set('tokens/a', 'adsf82983asd');
$activeBag->set('tokens/b', 'daslfl232l3k');
print_r($activeBag->get('tokens'));
Since Symfony 3, the override of session.attribute_bag.class parameter doesn't work anymore.
The solution I applied after pulling my hair for a few time is using a compiler pass to override the session.attribute_bag service class.
I did it in the Kernel directly, but an external compiler pass would work the same way.
SF4 Kernel
<?php
// src/Kernel.php
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
class Kernel extends BaseKernel implements CompilerPassInterface
{
use MicroKernelTrait;
// ...
public function process(ContainerBuilder $container)
{
$container->getDefinition('session.attribute_bag')->setClass(NamespacedAttributeBag::class);
}
}
With Symfony 4 (and Flex), use the following configuration to use NamespacedAttributeBag:
# config/services.yaml
services:
session.attribute_bag:
class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag
# ...
Update: Namespaced Sessions were removed in Symfony 6.0
See https://symfony.com/doc/5.4/session.html#basic-usage
The NamespacedAttributeBag class is deprecated since Symfony 5.3. If you need this feature, you will have to implement the class yourself.

Categories