How do you access Doctrine DBAL in a Symfony2 service class? - symfony

I'm learning Symfony2 (and OOP) and want to create a service that's available throughout my app. This service takes a value foo, checks it against a database table, and returns a value bar.
I have a little class
namespace Acme\TestBundle\Toolbox;
class StringToolbox
{
public function lookupSomething($foo)
{
$conn = $this->get('database_connection');
$sql = "SELECT bar FROM bar_list WHERE foo = :foo";
$stmt = $conn->prepare($sql);
$stmt->bindValue("foo", $foo);
$stmt->execute();
return $bar;
}
}
My settings are:
services:
toolbox:
class: Acme\TestBundle\Toolbox
arguments: [#database_connection]
But it throws an error saying that the get() method is undefined. I'm stuck-- how can I use DBAL in the service? Thanks!

First off you should add a constructor to your class and pass in the #doctrine.dbal.%connection_name%_connection service
namespace Acme\TestBundle\Toolbox;
use Doctrine\DBAL\Connection;
class StringToolbox
{
/**
*
* #var Connection
*/
private $connection;
public function __construct(Connection $dbalConnection) {
$this->connection = $dbalConnection;
}
public function lookupSomething($foo)
{
$sql = "SELECT bar FROM bar_list WHERE foo = :foo";
$stmt = $this->connection->prepare($sql);
$stmt->bindValue("foo", $foo);
$stmt->execute();
return $bar;
}
}
Your service configuration should now look like this:
parameters:
my_service_connection: default
services:
toolbox:
class: Acme\TestBundle\Toolbox\StringToolbox
arguments: [#doctrine.dbal.%my_service_connection%_connection]
What you are saying with this configuration is "make me a service named toolbox that will receive the doctrine.dbal.default_connection service as the first constructor argument"
There are other injection methods besides Constructor injection and you should read the http://symfony.com/doc/current/book/service_container.html documentation to get a grasp of all possibilities (Setter injection, Factory injection, etc) and to better understand how Dependency Injection works

#doctrine.dbal.connection not working, As Igor says, #doctrine.dbal.connection is an abstract, use #doctrine.dbal.default_connection if you only have one db connection, or #doctrine.dbal.%connection_name%_connection where the %connection_name% placeholder the name of the connection that you want to inject.
Your service configuration should now look like this:
services:
toolbox:
class: Acme\TestBundle\Toolbox\StringToolbox
arguments: [#doctrine.dbal.default_connection]

Related

What is the best way to display my entities at Back-office home page with the Sonata Admin Bundle

I am doing a back-office with the SONATA ADMIN BUNDLE and I was wondering how you get your different entities in the back-office side to get all my different objects at home-page. It's could be very great to have something like the demo : http://demo.sonata-project.org/admin/dashboard.
Has someone else experienced about this and can explain me?
Well, I guess you have a service defined in your services.yml. In that service you need to grab the information you want..
So you'll need some doctrine class, but your service doesn't have these.. You can inject them in your services.yml, for example:
sonata.block.service.statistics:
class: Evince\ObjectsBundle\Block\Service\StatisticsService
tags:
- { name: sonata.block }
arguments:
- ~
- '#templating'
calls:
- [ setDoctrine, ["#doctrine.orm.entity_manager"]] # <- here add the doctrine entity manager
In your service you have to implement an 'setDoctrine' function:
private $doctrine;
/**
* #param mixed $doctrine
*/
public function setDoctrine($doctrine)
{
$this->doctrine = $doctrine;
}
And a getDoctrine function:
/**
* #return mixed
*/
public function getDoctrine()
{
return $this->doctrine;
}
Now, when symfony builds your service when you need it, it will inject the doctrine entity manager for you.. This is called 'Dependency Indjection'.
In the execute function you can do something like this:
$repo = $this->getDoctrine()->getRepository('AcmeYourBundle:YourEntityClass');
$query = $repo->createQueryBuilder()
->from('foo', 'f')
->where('foo.bar = :id')
->setParameter('id', $someId)
->getQuery();
$results = $query->getResult();
$resultCount = count($results);
//-> pass the resultCount to your template
See the symfony website for information how to use doctrine.

Access configuration value inside a symfony 2 entity

What is the best way to access configuration values inside an entity in a symfony 2 application?
I've searched about this and i've found two solutions:
Define the entity as a service and inject the service container to access configuration values
And this approach which defines a class in the same bundle of the entity with static methods that allows to get the parameter value
Is there any other solution? What's the best workaround?
Your entity shouldn't really access anything else, apart from associated entities. It shouldn't really have any connection outwardly to the outside world.
One way of doing what you want would be to use a subscriber or listener to listen to the entity load event and then pass that value in to the entity using the usual setter.
For example....
Your Entity
namespace Your\Bundle\Entity;
class YourClass
{
private $parameter;
public function setParameter($parameter)
{
$this->parameter = $parameter;
return $this;
}
public function getParameter()
{
return $this->parameter;
}
...
}
Your Listener
namespace Your\Bundle\EventListener;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Your\Bundle\Entity\YourEntity;
class SetParameterSubscriber implements EventSubscriber
{
protected $parameter;
public function __construct($parameter)
{
$this->parameter = $parameter;
}
public function getSubscribedEvents()
{
return array(
'postLoad',
);
}
public function postLoad(LifecycleEventArgs $args)
{
/** #var YourEntity $entity */
$entity = $args->getEntity();
// so it only does it to your YourEntity entity
if ($entity instanceof YourEntity) {
$entity->setParameter($this->parameter);
}
}
}
Your services file.
parameters:
your_bundle.subscriber.set_parameter.class:
Your\Bundle\EventListener\SetParameterSubscriber
// Should all be on one line but split for readability
services:
your_bundle.subscriber.set_parameter:
class: %your_bundle.subscriber.set_parameter.class%
arguments:
- %THE PARAMETER YOU WANT TO SET%
tags:
- { name: doctrine.event_subscriber }
You shouldn't need a configuration in your entity.
For example you have File entity and you need to save a file represented by this entity to a disk. You need some parameter, let say "upload_dir". You can pass somehow this parameter to the entity and define a method inside this entity which saves a file to upload dir. But better way would be create a service which would be responsible for saving files. Then you can inject configurtion into it and in save method pass entity object as an argument.

Accessing generateURL() from Entity class

I would like to access generateUrl in my entity class. You can access generateUrl in controller class like this:
$url = $this->generateUrl('your_route_name', array(/* parameters */));
Accoring do this article, I should try to 'create a service, and inject the router component in it'. Then I am reading Symfony Docs: Service Container and try configuration, but I still can not make it.
in app/config/config.yml
services:
router:
class: ???
arguments: ???
How can I make router as service?
update
Why I want to use generateUrl in Entity class?
I am using Eko/FeedBundle. It requires to implements getFeedItemLink() in Entity. I need to give URL as return value of this function.
The short answer is: you don't use the router inside entities and you don't create entities as services. You should create a separate service which is responsible for creating urls (if you wrote more about what you are trying achieve it would be simpler to give you more appropriate example).
For example:
use Symfony\Bundle\FrameworkBundle\Routing\Router;
class YourCustomUrlGenerator
{
private $router;
public function __construct(Router $router)
{
$this->router = $router;
}
public function generateUrl(YourEntity $entity)
{
// generate the url
}
}
Then define your service for DIC:
services:
custorm_url_generator:
class: Namespace\YourCustomUrlGenerator
arguments: [#router]

How should I configure an object dynamically when using Symfony's DI?

I'm using a YAML configuration to wire my dependencies, and I need to provide some runtime information to get a useful object back. I was going to run a setter method from my code once the object has been injected, but I Was wondering if there was a better way of doing it (or if there's something I'm missing).
This is the gist of my configuration:
services:
example_object : "myObject"
arguments : ["%object_parameter1%"]
parameters:
object_parameter1 : Some Static Data
object_parameter2 : #Rutime info required
For retrieving the current logged in user in any service, inject the security.context. In this case I use setter injection to simply user mock injection.
namespace Acme\ExampleBundle\Foo;
use Symfony\Component\Security\Core\SecurityContextInterface;
class MyService
{
private $param;
private $user;
public function __construct($param)
{
$this->param = $param;
}
/**
* Retrieve the current logged in user from the security context.
*/
public function setUserFromContext(SecurityContextInterface $context)
{
$this->user = $context->getToken()->getUser();
}
/**
* Set any user object.
*
* Usefull for testing, to inject a simple user mock.
*/
public function setUser($user)
{
$this->user = $user;
}
public function doSomething()
{
// do something with the user object
}
}
Define the service:
services:
my_service:
class: Acme\ExampleBundle\Foo\MyService
arguments: ["%object_parameter1%"]
calls:
- [ setUserFromContext, [#security.context] ]
You should not try to add dynamic values directly into the DI configuration. Symfony services configuration is reflected by compiled DI container and recompilation is very heavy operation.
If you do not want to couple your service with Symfony's security system directly, you can add your custom "user provider" service as a dependency. Then you will need to rewrite this service if the source of information will change. It may be also easily mocked.
You can also use a factory to inject a user object instead of user provider service.

Custom Constraint Validator with Database Connection Dependency Symfony2

I'm adding a custom validation query to a Symfony2 project.
The docs lack a complete example, and I'm not sure how to actually inject the database connection into the Validator Class. I've created the service in my config, added the validatedBy alias method in my Constraint class, and set up this in my Validator Class:
use Doctrine\DBAL\Connection;
class ZipDatabaseValidator extends ConstraintValidator
{
/**
*
* #var Connection
*/
private $connection;
public function __construct(Connection $dbalConnection) {
$this->connection = $dbalConnection;
}
public function validate($zipcode, Constraint $constraint)
{
$sql = 'SELECT * FROM zip_table WHERE zip_code = ?';
$stmt = $this->connection->prepare($sql);
...
Here's my service config:
validator.node.zip_in_database:
class: Acme\Bundle\Validator\Constraints\ZipDatabaseValidator
arguments: [#database_connection]
tags:
- { name: validator.constraint_validator, alias: zip_in_database }
I keep getting errors, in this case:
Catchable Fatal Error: Argument 1 passed to
Acme\Bundle\Validator\Constraints\ZipDatabaseValidator::__construct()
must be an instance of Doctrine\DBAL\Connection, none given,
How the heck to I set this up as a service or otherwise inject the database connection?
validator.node.zip_in_database:
class: Acme\Bundle\Validator\Constraint\ZipDatabaseValidator
arguments: [#database_connection]
tags:
- { name: validator.constraint_validator, alias: zip_in_database }
You must pass doctrine as an argument to your Service.
Edit
Make sure the alias is the same as the validatedBy method returns!
in your case:
//Acme\Bundle\Validator\Constraint\ZipDatabase class
public function validatedBy()
{
return 'zip_in_database';
}

Resources