Am using symfony framework for my application. And to save records in database I want call the $this->getDoctrine()->getManager(); method in my entity class. But when I did that it gave me the error:
Call to undefined method getDoctrine(),
Can some one tell me what is the right way to do this.
My entity class is like:
namespace Acme\SuperbAppBundle\Entity;
use Symfony\Component\DependencyInjection\Container;
use Doctrine\ORM\Mapping as ORM;
class Users
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $firstName;
/**
* #var string
*/
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
* #return Users
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
function __construct($firstName){
$this->setFirstName($firstName);
}
function save(){
$em = $this->getDoctrine()->getManager();
$em->persist($create);
$em->flush();
}
}
And my controller method is like:
public function test(){
$create = new Users('Rajat');
$create->save();
}
Your save method is attempting to call
$this->getDoctrine();
Whereby $this is the current Class, and any other Class it inherits. As it stands, your current Class, User, is standalone, and does not have a getDoctrine() method. If your Class were to extend the Controller Class, it would have access to that method:
class User extends Controller
I believe this simple fix will work, although it probably doesn't make real sense for it to extend Controller, as it is a User Entity, and unrelated to a Controller. A preferred, more advanced method, would be to inject the Doctrine service into the User class.
Ok, first of all Doctrine Entities :
Handle the entity generation and configuration
Declare the operations on the setters and getters.
If you wana save an object into your entity there it's your User, you have two way to store this user:
One:
You can use entity manager to store a user and the entity will help you to create the right object using the seters and getters:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use PATH\TO\Users;
class ExampleController extends Controller
{
public function examplefunction()
{
$em = $this->getDoctrine()->getManager();
$entity = new Users();
$entity->setFirstName('Rajat');
$em->persist($entity);
$em->flush();
}
}
The other way is to create this entry using QueryBuilder but it's a bad way in your case.
Oh, i forgot please delete the save method in your entity Doctrine manager allready implement it.
Your controller probably doesnt extends Symfony\Bundle\FrameworkBundle\Controller\Controller ...
You should have controller defined like this example:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
}
Entity class does not extends ContainerAware / Controller, so you can't call $this->getDoctrine()->getManager(). I don't think your Entity class should extend to a Controller. Because your entity class will become a controller instance just because you want to access the doctrine manager. That's a not good practice. What you can do is inject doctrine manager to your Entity class through services.
I wrote a blog few weeks ago regarding injecting services container and accessing through constructor. You can inject doctrine entity manager in the same way you inject services container. You can take a look at that if you like :- http://anjanasilva.com/blog/injecting-services-in-symfony-2/
Here's a nice question regarding injecting doctrine manager. Make sure you read the answer as well. :- Symfony 2 EntityManager injection in service
And another nice tutorial on injecting custom repository manager instead of injecting the whole entity manager. Which I believe even a good solution. :- http://php-and-symfony.matthiasnoback.nl/2014/05/inject-a-repository-instead-of-an-entity-manager/
Hope this helps to increase your understanding about Symfony 2.
Cheers!
Related
I am adherent of Action Class approach using instead of Controller. The explanation is very simple: very often Controller includes many actions, when following the Dependency Injection principle we must pass all required dependencies to a constructor and this makes a situation when the Controller has a huge number of dependencies, but in the certain moment of time (e.g. request) we use only some dependencies. It's hard to maintain and test that spaghetti code.
To clarify, I've already used to work with that approach in Zend Framework 2, but there it's named Middleware. I've found something similar in API-Platform, where they also use Action class instead of Controller, but the problem is that I don't know how to cook it.
UPD:
How can I obtain the next Action Class and replace standard Controller and which configuration I should add in regular Symfony project?
<?php
declare(strict_types=1);
namespace App\Action\Product;
use App\Entity\Product;
use Doctrine\ORM\EntityManager;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class SoftDeleteAction
{
/**
* #var EntityManager
*/
private $entityManager;
/**
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* #Route(
* name="app_product_delete",
* path="products/{id}/delete"
* )
*
* #Method("DELETE")
*
* #param Product $product
*
* #return Response
*/
public function __invoke(Request $request, $id): Response
{
$product = $this->entityManager->find(Product::class, $id);
$product->delete();
$this->entityManager->flush();
return new Response('', 204);
}
}
The question is a bit vague for stackoverflow though it's also a bit interesting. So here are some configure details.
Start with an out of the box S4 skeleton project:
symfony new --version=lts s4api
cd s4api
bin/console --version # 4.4.11
composer require orm-pack
Add the SoftDeleteAction
namespace App\Action\Product;
class SoftDeleteAction
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function __invoke(Request $request, int $id) : Response
{
return new Response('Product ' . $id);
}
}
And define the route:
# config/routes.yaml
app_product_delete:
path: /products/{id}/delete
controller: App\Action\Product\SoftDeleteAction
At this point the wiring is almost complete. If you go to the url you get:
The controller for URI "/products/42/delete" is not callable:
The reason is that services are private by default. Normally you would extend from AbstractController which takes care of making the service public but in this case the quickest approach is to just tag the action as a controller:
# config/services.yaml
App\Action\Product\SoftDeleteAction:
tags: ['controller.service_arguments']
At this point you should have a working wired up action.
There of course many variations and a few more details. You will want to restrict the route to POST or fake DELETE.
You might also consider adding an empty ControllerServiceArgumentsInterface and then using the services instanceof functionality to apply the controller tag so you no longer need to manually define your controller services.
But this should be enough to get you started.
The approach I was trying to implement is named as ADR pattern (Action-Domain-Responder) and Symfony has already supported this started from 3.3 version. You can refer to it as Invokable Controllers.
From official docs:
Controllers can also define a single action using the __invoke() method, which is a common practice when following the ADR pattern (Action-Domain-Responder):
// src/Controller/Hello.php
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/hello/{name}", name="hello")
*/
class Hello
{
public function __invoke($name = 'World')
{
return new Response(sprintf('Hello %s!', $name));
}
}
I'm trying to validate my entity via static callback.
I was able to make it work following the Symfony guide but something isn't clear to me.
public static function validate($object, ExecutionContextInterface $context, $payload)
{
// somehow you have an array of "fake names"
$fakeNames = array(/* ... */);
// check if the name is actually a fake name
if (in_array($object->getFirstName(), $fakeNames)) {
$context->buildViolation('This name sounds totally fake!')
->atPath('firstName')
->addViolation()
;
}
}
It works fine when I populate my $fakeNames array but what if I want to make it "dynamic"? Let's say I want to pick that array from the parameters or from the database or wherever.
How am I supposed to pass stuff (eg. the container or entityManager) to this class from the moment that the constructor doesn't work and it has to be necessarily static?
Of course my approach may be completely wrong but I'm just using the symfony example and few other similar issues found on the internet that I'm trying to adapt to my case.
You can create a Constraint and Validator and register it as service so you can inject entityManager or anything you need, you can read more here:
https://symfony.com/doc/2.8/validation/custom_constraint.html
or if you are on symfony 3.3 it is already a service and you can just typehint it in your constructor:
https://symfony.com/doc/current/validation/custom_constraint.html
This is the solution I was able to find in the end.
It works smoothly and I hope it may be useful for someone else.
I've set the constraint on my validation.yml
User\UserBundle\Entity\Group:
constraints:
- User\UserBundle\Validator\Constraints\Roles\RolesConstraint: ~
Here is my RolesConstraint class
namespace User\UserBundle\Validator\Constraints\Roles;
use Symfony\Component\Validator\Constraint;
class RolesConstraint extends Constraint
{
/** #var string $message */
public $message = 'The role "{{ role }}" is not recognised.';
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}
and here is my RolesConstraintValidator class
<?php
namespace User\UserBundle\Validator\Constraints\Roles;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class RolesConstraintValidator extends ConstraintValidator
{
/** #var ContainerInterface */
private $containerInterface;
/**
* #param ContainerInterface $containerInterface
*/
public function __construct(ContainerInterface $containerInterface)
{
$this->containerInterface = $containerInterface;
}
/**
* #param \User\UserBundle\Entity\Group $object
* #param Constraint $constraint
*/
public function validate($object, Constraint $constraint)
{
if (!in_array($object->getRole(), $this->containerInterface->getParameter('roles'))) {
$this->context
->buildViolation($constraint->message)
->setParameter('{{ role }}', $object->getRole())
->addViolation();
}
}
}
Essentially, I set up a constraint which, every time a new user user is registered along with the role, that role must be among those set in the parameters. If not, it builds a violation.
All of my query in Entity Repository needs to be filtered by user.
Now I want to know how can I access the currently logged in user in Entity Repository directly.
What I did today is to get the currently logged in user in my controller, through the use of $this->getUser() and then pass it to Entity Repository and this is not efficient.
You need to inject security.token_storage service into another one to get the current user, but as of Repository classes belong to Doctrine project, not Symfony, it is not recommended to do this.. May be there is a way to achieve it by creating custom entityManager class as described here, but I don't think it would a good solution..
Instead of customizing an entityManager better create a service which calls repository classes' methods, inject desired services into it.. Let Repository classes do their job.
Implementation would be something like this:
RepositoryClass:
class MyRepository extends EntityRepository
{
public function fetchSomeDataByUser(UserInterface $user)
{
// query
}
}
Service:
class MyService
{
private $tokenStorage;
public function _construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
// other services
}
public function getSomeDataByUser()
{
$user = $this->tokenStorage->getToken()->getUser();
return $this->entityManager->getRepository(MyREPOSITORY)->fetchSomeDataByUser($user);
}
}
Usage:
public function someAction()
{
$dataByUser = $this->get(MYSERVICE)->getSomeDataByUser();
}
If you use JMSDiExtraBundle it can be done by adding setter injection:
use Doctrine\ORM\EntityRepository;
use JMS\DiExtraBundle\Annotation as DI;
class YourRepository extends EntityRepository
{
/** #var User current user entity */
protected $user;
/**
* #DI\InjectParams({
* "token_storage" = #DI\Inject("security.token_storage")
* })
*/
public function setSimplaManager(TokenStorageInterface $tokenStorage)
{
$token = $tokenStorage->getToken();
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
$this->user = $user;
}
}
I'd like to use, something like:
$em = $this->getEntityManager();
Inside a Entity.
I understand I should do this as a service but for some testing purposes, I want to access it from an Entity.
Is it possible to achieve that?
I've tried to:
$em = $this->getEntityManager();
$profile_avatar = $em->getRepository('bundle:Perfils')->findOneByUser($this-getId());
But isn't working.
Fatal error: Call to undefined method
Proxies\webBundleEntityUserProxy::getEntityManager() in
/opt/lampp/htdocs/web/src/Pct/bundle/Entity/User.php on line
449
Why am I trying to do it this way?
I've 3 kinds of users: Facebook, Twitter and MyOwnWebsite users. Each of them have differents avatar which links facebook's profile, twitter's or otherwise, if its myownwebsite user, I retrieve the avatar from a URL in a database. For now, I don't want to create a service, because I'm just trying to make it working, to test it, not to create a final deployment. So this is why I'm trying to call Entity manager from an Entity. I don't want, by now, to modify configuration files, just this entity.
As pointed out (again) by a commenter, an entity manager inside an entity is a code smell. For the OP's specific situation where he wished to acquire the entity manager, with the least bother, a simple setter injection would be most reliable (contrary to my original example injecting via constructor).
For anyone else ending up here looking for a superior solution to the same problem, there are 2 ways to achieve this:
Implementing the ObjectManagerAware interface as suggested by https://stackoverflow.com/a/24766285/1349295
use Doctrine\Common\Persistence\ObjectManagerAware;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Entity implements ObjectManagerAware
{
public function injectObjectManager(
ObjectManager $objectManager,
ClassMetadata $classMetadata
) {
$this->em = $objectManager;
}
}
Or, using the #postLoad/#postPersist life cycle callbacks and acquiring the entity manager using the LifecycleEventArgs argument as suggested by https://stackoverflow.com/a/23793897/1349295
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class Entity
{
/**
* #ORM\PostLoad
* #ORM\PostPersist
*/
public function fetchEntityManager(LifecycleEventArgs $args)
{
$this->setEntityManager($args->getEntityManager());
}
}
Original answer
Using an EntityManager from within an Entity is VERY BAD PRACTICE. Doing so defeats the purpose of decoupling query and persist operations from the entity itself.
But, if you really, really, really need an entity manager in an entity and cannot do otherwise then inject it into the entity.
class Entity
{
private $em;
public function __contruct($em)
{
$this->em = $em;
}
}
Then invoke as new Entity($em).
Best way is to use Life Cycle: #ORM\HasLifecycleCallbacks
And you can use the appropriate Event as you want to get result:
#postLoad
#postPersist
...
Calling the Entity Manager from inside an Entity is a bad practice! You should keep your entities as simple as possible.
For what purpose do you need to call the Entity Manager from an Entity?
What I think you should do is, instead of using the Entity Manager inside your entity, is to create a custom repository for your entity.
In your entity ORM file, add an entry as follows (or in your entity class annotations if not using YML):
App\Bundle\Profils:
# Replace the above as appropiate
type: entity
table: (your table)
....
repositoryClass: App\Bundle\CustomRepos\ProfilsRepository
# Replace the above as appropiate.
# I always put my custom repos in a common folder,
# such as CustomRepos
Now, create a new PHP class that has the namespace above:
//Your ProfilsRepository.php
<?php
namespace App\Bundle\CustomRepos;
use Doctrine\ORM\EntityRepository;
class ProfilsRepository extends EntityRepository
{
/**
* Will return the user url avatar given the user ID
* #param integer $userID The user id.
#return string The avatar url
*/
public function getUserProfile($userId)
{
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
$qb->select... (your logic to retrieve the profil object);
$query = $qb->getQuery();
$result = $query->getResult();
return $result;
}
}
Finally, in your Controller:
// Your controller
<?php
namespace <class namespace>;
...
use App\Bundle\CustomRepos\ProfilsRepository;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
...
class YourClassNameController extends Controller
{
public function yourAction()
{
$userId = <get the user ID>;
// Pass the name of your entity manager to the
// getManager function if you have more than one and
// didn't define any default
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('Profils');
$avatar = $repo->getUserProfile($userId);
...
}
}
You need to set the services.yml with:
services:
your_service_name:
class: AppBundle\Controller\ServiceController
arguments: [ #doctrine.orm.entity_manager ]
You need to set also the Controller with the following constructor:
public function __construct(\Doctrine\ORM\EntityManager $em)
{
$this->em = $em;
}
and use $this->em in the controller
(for example $connection = $this->em->getConnection();)
I´ve created my own user Bundle, extending FOSUserBundle.
In my UserBundle, I have a memberID field, with its setters and getters.
I can already find users by memberID using EntityManager, and then I could find the user through UserManager matching the username/email/... obtained with that EntityManager query, but...
Is there a way to findUserByMemberID using UserManager?
Thank you.
Thank you for your replies. It seems it´s easier than all this stuff.
OwnUserBundle:
/**
* #ORM\Column(type="string")
*
*/
protected $memberID;
public function getMemberID()
{
return $this->memberID;
}
public function setMemberID($memberID)
{
$this->memberID = $memberID;
}
You can query FOSUserBundle:
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('memberID' => '123'));
Then, using method findUserBy(array(*OwnUserBundle_field* => *search_parameter_value*)) you´ll get the user/s instance.
Every query that is "not standard" one has to be written into a repository class
You have also to relate this class with one that represent you data model.
Suppose that your entity is called User, you have to do something like this
/**
* VendorName\UserBundle\Entity\User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="VendorName\UserBundle\Repository\UserRepository")
*/
class User implements AdvancedUserInterface
{
[...]
This says that every "custom" query for that entity will be fit into that repository class.
Now you have to create the repository
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function findUserByMemberID($id)
{
/* your logic here */
}
[...]
and you can use that in the following way
$userRepo = $this->em->getRepository('VendorUserBundle:User');
$userRepo->findUserByMemberID();
You could extend the UserManager from the FOSUserBundle in your bundle and write your own method. And you could follow these instructions http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes