I have a choice field (drop-down) which I want to validate against a DB table.
Essentially, if the value is in the query's results, it's valid.
It's not so clear to me how the callback reported in the Symfony guide works :(
However, I have a validation.yml file:
User\UserBundle\Entity\Group:
properties:
role:
- Choice:
groups: [signUp]
callback: [User\UserBundle\Entity\Group, getRoles]
The entity Group.php
class Group
{
/** #var int */
private $id;
//...
public static function GetRoles()
{
return ['admin', 'user'];
}
}
This example works fine but my issue comes when I try to get those values from the group repository GroupRepository.php
class GroupRepository extends EntityRepository
{
public function getRoles()
{
return $this->createQueryBuilder('r')
->getQuery()
->getResult();
}
}
What am I supposed to do at this stage? Is the approach I used correct or should I call the Group Repository directly in the validation.yml? Or am I totally way off?
As I understand it you are trying to get those options from the repository like:
...
callback: [User\UserBundle\Repository\GroupRepository, getRoles]
This won't work as the Repository needs to be initialized through the Doctrine ORM service.
I guess you have to create a custom Constraint class and ConstraintValidator where the later is configured as a service and gets the entity manager passed as argument.
See http://symfony.com/doc/current/validation/custom_constraint.html
Related
This is a Symfony 3 project.
In User entity, i need to implement the method getRoles(). I have a private member $roles that is an array and I added it into serialize and unserialize methods.
public function getRoles()
{
if (count($this->roles) == 0) {
$this->roles = { ... read from db ... };
}
return $this->roles;
}
A issue I'm facing is that in ... read from db ... part, I have to use some parameters from parameters.yml. Usually, $this->container->getParameter(...) does the job. Unfortunately, from an entity I have no access to the container.
My question is: How can I access parameters.yml from an Entity?
Can I somehow inject the required parameters?
Another question is: do I need to serialize $roles as well or should they be read on every request?
--- EDIT ---
That logic seems to me correctly placed.
getRoles() function is supposed to get user's role to Security bundle. It accomplishes it by querying private members and ORM relations. The only problem is that I need do identify certain groups, as they don't have similar names in all deployments. Thats why I need the parameters.yml.
Here is a fragment from User entity, which implements AdvancedUserInterface.
public function getRoles() {
$ADMIN_GRP = "ADMIN_GROUP"; // I need this from parameters.yml
$SUPPORT_GRP = "SUPPORT_GROUP"; // I need this from parameters.yml
$roles = ['ROLE_USER'];
foreach ($this->memberships as $m) {
if ($m->getGroupId() == $SUPPORT_GRP)
array_push($roles, "ROLE_SUPPORT");
if ($m->getGroupId()) == $ADMIN_GRP)
array_push($roles, "ROLE_ADMIN");
}
return $roles;
}
as malcolm said, you should not be touching the EntityManager, from inside your entity, that logic is NOT correctly placed.
also, you should not read parameters.yml from inside your entity
(you COULD)
use Symfony\Component\Yaml\Yaml;
$value = Yaml::parse(file_get_contents('/path/to/file.yml'));
but you really SHOULDNT use the above approach
(you could also add constants to the user entity ...)
Why not adding a group label to your Membership entity ? So you can do...
public function getRoles() {
$roles = ['ROLE_USER'];
foreach ($this->memberships as $m) {
if ($m->getGroupRole() == 'ROLE_SUPPORT')
array_push($roles, "ROLE_SUPPORT");
if ($m->getGroupRole()) == 'ROLE_ADMIN')
array_push($roles, "ROLE_ADMIN");
}
return $roles;
}
Consider the following situation. I have an entity that holds some information, let's say a news item. This news item contains comments.
In the news item entity, there is calculateStatistics() function, that returns some statistics derived from the news item entity, plus its comments. I used to have this calculate function inside a NewsService, but then found out a service wasn't needed because I only use information that is inside the entity.
Nowadays, the calculate function also does some sanity checking. I want to log negative results as a warning in my Monolog service. I still believe at this point it is valid to have this calculate function inside the entity, since no external information/service is needed. Is there an elegant way to support logging inside an entity?
I don't think that handling logging inside Entity is a good idea, as entity should be as independent as possible and have no business logic inside. I would suggest doing it by event listener. Consider such configuration (I assume you're using Doctrine and want to perform logging while some doctrine event - but if not, you will only have to modify name of event which you listen to):
Entity:
class YourEntity implements StatisticInterface
{
(...)
public function calculateStatistics()
{
(...)
}
}
config.yml
your_service.statistics_listener:
class: Acme\DemoBundle\EventListener\Entity\StatisticsEntityListener
arguments: [#logger]
tags:
- { name: doctrine.event_listener, event: prePersist }
prePersist is one of many possible events, just pick one that fits most
StatisticsEntityListener
class StatisticsEntityListener
{
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
/**
* #param LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof StatisticInterface) {
//do whatever you like with your logger and entity
$logger->log($entity->calculateStatistics());
}
}
}
This way you get nice separation of concerns and you're able to log info using your Monolog
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.
We have such repository
class CronInfoCharacterInfoRepository extends EntityRepository
{
/**
* #param string|integer $characterID
* #return void
*/
public function add($characterID)
{
if (!$this->find($characterID)) {
$oEntry = new CronInfoCharacterInfo();
$oEntry->setCharacterID($characterID);
$this->getEntityManager()->persist($oEntry);
$this->getEntityManager()->flush();
}
}
}
But I want insert new entry by DQL. How can I do this through $this->createQueryBuilder()... ? And is it normal to use 'entity manager' inside repository?
You cant insert using DQL the very documentation says about that
INSERT statements are not allowed in DQL, because entities and their
relations have to be introduced into the persistence context through
EntityManager#persist() to ensure consistency of your object model.
http://docs.doctrine-project.org/en/latest/reference/dql-doctrine-query-language.html
And it is perfectly ok to use enitity manager inside Repo. What you are doing now is correct !
I am starting with Symfony2 and I am trying to override FOS\UserBundle\Form\Handler\RegistrationFormHandler of FOSUserBundle.
My code is:
<?php
namespace Testing\CoreBundle\Form\Handler;
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Form\Handler\RegistrationFormHandler as BaseHandler;
use Testing\CoreBundle\Entity\User as UserDetails;
class RegistrationFormHandler extends BaseHandler
{
protected function onSuccess(UserInterface $user, $confirmation)
{
// I need an instance of Entity Manager but I don't know where get it!
$em = $this->container->get('doctrine')->getEntityManager();
// or something like: $em = $this->getDoctrine()->getEntityManager
$userDetails = new UserDetails;
$em->persist($userDetails);
$user->setId($userDetails->getId());
parent::onSuccess($user, $confirmation);
}
}
So, the point is that I need an instance of Doctrine's Entity Manager but I don't know where/how get it in this case!
Any idea?
Thanks in advance!
You should not use EntityManager directly in most of the cases. Use a proper manager/provider service instead.
In case of FOSUserBundle service implementing UserManagerInterface is such a manager. It is accessible through fos_user.user_manager key in the service container (which is an allias to fos_user.user_manager.default). Of course registration form handler uses that service, it is accessible through userManager property.
You should not treat your domain-model (i.a. Doctrine's entities) as if it was exact representation of the database-model. This means, that you should assign objects to other objects (not their ids).
Doctrine is capable of handling nested objects within your entities (UserDetails and User objects have a direct relationship). Eventually you will have to configure cascade options for User entity.
Finally, UserDetails seems to be a mandatory dependency for each User. Therefore you should override UserManagerInterface::createUser() not the form handler - you are not dealing with user's details there anyway.
Create your own UserManagerInterface implementation:
class MyUserManager extends \FOS\UserBundle\Entity\UserManager {
/**
* {#inheritdoc}
*/
public function createUser() {
$user = parent::createUser();
$user->setUserDetails(new UserDetails());
// some optional code required for a proper
// initialization of User/UserDetails object
// that might require access to other objects
// not available inside the entity
return $user;
}
}
Register your own manager as a serive inside DIC:
<service id="my_project.user_manager" class="\MyProject\UserManager" parent="fos_user.user_manager.default" />
Configure FOSUserBundle to use your own implementation:
# /app/config/config.yml
fos_user:
...
service:
user_manager: my_project.user_manager