In Symfony, I found three ways for accessing doctrine service and entity manager as follow:
$em = $this->getDoctrine()->getManager();
$em = $this->get('doctrine')->getEntityManager();
$em = $this->container->get('doctrine.orm.entity_manager');
Can anyone kindly explain their differences please and explain when should we use which of them.
The first one is only available when extending the base controller. It's a shortcut to doing $this->get('doctrine'), as you can see in the source:
public function getDoctrine()
{
if (!$this->container->has('doctrine')) {
throw new \LogicException('The DoctrineBundle is not registered in your application.');
}
return $this->container->get('doctrine');
}
$this->get('doctrine') is also only available in controllers. get is also defined in the base controller and is a shortcut for $this->container->get():
public function get($id)
{
return $this->container->get($id);
}
$this->container->get('doctrine') is the fully written form of getting the doctrine registry.
$this->get('doctrine') its the method to use services,
And in symfony you have shortcutes to call this service $this->getDoctrine()
Related
I am using FOSUserBundle to be able to manage users in a symfony2 project.
Since using container is not recommended my question is how can I extend the FOSUserBundle to be able to create a custom save method like this, for example:
class UserRepository extends EntityRepository
{
public function registration(array $data)
{
// example only
$userManager = $this->container->get('fos_user.user_manager');
//$em = $this->container->get('doctrine')->getEntityManager();
//$userUtils = $this->container->get('fos_user.util.token_generator');
$user = $userManager->createUser();
$user->setFirstName($data['first_name']);
$user->setLastName($data['last_name']);
$user->setEmail($data['user_email']);
$user->setUsername($data['user_email']);
$user->setPlainPassword($data['user_password']);
$user->setEnabled(false);
$user->setConfirmationToken($userUtils->generateToken());
$user->addRole('ROLE_USER');
$em->persist($user);
$em->flush();
}
Would it be smart to pass the $userManager and $userUtils objects in the controller when using the method?
I think the better is to override the FosUser Controller Action (Register for example) and put your code in a specific service.
The symfony2 doc give a great sample: http://symfony.com/doc/current/cookbook/bundles/inheritance.html
I'm very new in development and symfony. I wonder me what's the best way to code this below.
I've 2 entities (user and account). There is a relation between them (create an account requiers a user).
I wonder me what is the best way to set the user in account entity (prepersist, controller, __construct) when I'm adding an new account ?
PREPERSIST
First, I didn't find anything to set the user with prepersit method. Is there a way ?
Something like that :
/**
* #ORM\PrePersist
*/
public function prePersist()
{
$this->user = $this->get('security.context')->getToken()->getUser();
$this->updatedAt = new \Datetime("now");
$this->isActive = false;
}
CONTROLLER
...
$user = new User();
$account = new Account();
$account->setUser($user);
...
CONSTRUCTOR
/* Entity account */
...
public function __construct($user)
{
$this->user = $user;
}
...
/* Controller account */
...
$account = new Account($this->get('security.context')->getToken()->getUser())
...
Hope you can help me.
Based on your code above, you don't need to hook into a doctrine event to accomplish what you want. You can create the association in the controller before persisting the Account object.
If you are using the Symfony security component, obtaining the user in the controller is as simple as $this->getUser(). You can inject User via the Account constructor method __construct($user) or a setter method setUser($user). Although the constructor method is more efficient, either way is correct.
And to persist the Account object to your database from within the controller:
$em = $this->getDoctrine()->getManager();
$em->persist($account);
$em->flush();
I would recommend creating Doctrine2 Listener / Subscriber and register it as a service, than listen to prePersist event of Account entity. You can inject any other needed services in listeners / subscribers.
All information you need can be found on: http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html
How can I achieve this:
For example, I have an entity called Issue. I need to log changes of a field of this entity.
If a user changes the field "status" on the Issue entity I need to create a database record about it with the user, who changed the field, the previous status and the new status.
Using: Symfony2 + doctrine2.
You can use an event subscriber for that, and attach it to the ORM event listener (in symfony 2, there's docs about that):
namespace YourApp\Subscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use YourApp\Entity\Issue;
use YourApp\Entity\IssueLog;
class IssueUpdateSubscriber implements EventSubscriber
{
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $updated) {
if ($updated instanceof Issue) {
$em->persist(new IssueLog($updated));
}
}
$uow->computeChangeSets();
}
public function getSubscribedEvents()
{
return array(Events::onFlush);
}
}
You can eventually check the changeset as I've explained at Is there a built-in way to get all of the changed/updated fields in a Doctrine 2 entity.
I left the implementation of IssueLog out of the example, since that is up to your own requirements.
Is it possible to run all doctrine queries through a walker of some sort so that I can modify the query based on the current user's credentials? Ideally, I wouldn't have to explicitly call a setHint for a custom walker on every query, as that would restrict my ability to pass the current SecurityContext into the walker.
Also, I'd prefer not to use a Doctrine Filter, as I can't modify join conditions with filters, and I'd be forced to use an "IN" clause, which would severely affect performance
Currently, I'm using a service that modifies the QueryBuilder based on a user's credentials, but this becomes tedious, as I need to call the service every time I create a new QueryBuilder, and is even more of a pain when Repositories come into play (as I'd need to inject the service into every repository that needs to modify the query.
Hopefully I've explained this clearly enough. Appreciate any feedback!
I think I have solved my own issue. If someone else has a more elegant way of doing achieving these results, feel free to explain. In order to modify all of my queries, I have created a custom EntityManager and custom EntityRepository.
In my custom EntityManager, I have overwritten 2 methods. create() and getRepository()
public static function create($conn, Configuration $config, EventManager $eventManager = null)
{
if ( ! $config->getMetadataDriverImpl()) {
throw ORMException::missingMappingDriverImpl();
}
switch (true) {
case (is_array($conn)):
$conn = \Doctrine\DBAL\DriverManager::getConnection(
$conn, $config, ($eventManager ?: new EventManager())
);
break;
case ($conn instanceof Connection):
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw ORMException::mismatchedEventManager();
}
break;
default:
throw new \InvalidArgumentException("Invalid argument: " . $conn);
}
return new MyCustomEntityManager($conn, $config, $conn->getEventManager());
}
The only thing that is changed in this method is that I am returning my own EntityManger(MyCustomEntityManager). Then, I overlaid the getRepository method as follows:
public function getRepository($entityName)
{
$entityName = ltrim($entityName, '\\');
if (isset($this->repositories[$entityName])) {
return $this->repositories[$entityName];
}
$metadata = $this->getClassMetadata($entityName);
$repositoryClassName = $metadata->customRepositoryClassName;
if ($repositoryClassName === null) {
$repositoryClassName = "Acme\DemoBundle\Doctrine\ORM\MyCustomEntityRepository";
}
$repository = new $repositoryClassName($this, $metadata);
$this->repositories[$entityName] = $repository;
return $repository;
}
Here, I have only modified one line as well. Instead of relying on the DBAL Configuration to retreive the default $repositoryClassName, I have specified my own default repository Acme\DemoBundle\Doctrine\ORM\MyCustomEntityRepository.
Once you have created your own custom EntityRepository, the sky is the limit. You can inject services into the repository(I currently use JMS Di annotations, described below), or perform custom actions against a QueryBuilder in the createQueryBuilder method, like so:
use JMS\DiExtraBundle\Annotation as DI;
class MyCustomEntityRepository extends EntityRepository
{
private $myService;
public function createQueryBuilder($alias)
{
$queryBuilder = parent::createQueryBuilder($alias);
/** INSERT CUSTOM CODE HERE **/
return $queryBuilder;
}
/**
* #DI\InjectParams({
* "myService" = #DI\Inject("my_service_id")
* })
*/
public function setMyService(MyServiceInterface $myService)
{
$this->myService = $myService;
}
}
Once you have created your own EntityRepository, you should have all of your repositories that need this custom functionality extend MyCustomEntityRepository. You could even take it a step further and create your own QueryBuilder to further extend this.
You can write a custom AST Walker and setup your application to use this walker for all queries with defaultQueryHint (Doctrine 2.5 new feature) configuration option:
<?php
/** #var \Doctrine\ORM\EntityManager $em */
$em->getConfiguration()->setDefaultQueryHint(
Query::HINT_CUSTOM_TREE_WALKERS,
['YourWalkerFQClassName']
)
I'm following Symfony2's form processing:
public function createAction()
{
$entity = new Node();
$request = $this->getRequest();
$form = $this->createForm(new NodeType(), $entity);
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($entity);
$em->flush();
The problem is the the "Node" entity has some other fields that aren't populated by the user, but rather by processes in the controller script. These "system generated" values should also be persisted along with the the "user generated" values from the form.
I'm not sure how to add the system values to the entity.
It is a shortcoming in my OOP knowledge, but I can't find any examples in the docs or online. Thanks!
I think you need to add some getXXX/setXXX methods to Node class (or look inside class'es code for them), so your code will look like
$em = $this->getDoctrine()->getEntityManager();
$entity->setPropertyOne('some value of mine');
$entity->setCurrentUserId($this->get('security.context')
->getToken()->getUser()->getId());
// another entity setters
$em->persist($entity);
Don't know if it would help you in your case, but i suggest reading about Doctrine 2 events here.
I also strongly recommend you reading this (unofficial) Symfony2 book :)
If you want to manage it in entity rather in controller. And if you are ising YML then just add this on YML file
lifecycleCallbacks:
prePersist: [ doPrePersist ]
and in the entity just add this method
function doPrePersist()
{
$this->publish = true;
$this->isDeleted = false;
}
If you are using annotation then in the entity just add the annotation tag
/**
* #ORM\prePersist
*/
function doPrePersist()
{
$this->publish = true;
$this->isDeleted = false;
}