Coming from a NodeJS environment, this seems like a nobrainer but I somehow did not figured it out.
given the function:
/**
* #Route("/", name="create_stuff", methods={"POST"})
*/
public function createTouristAttraction($futureEntity): JsonResponse
{
...
}
Let futureEntity have the same structure as my PersonEntity.
What is the best way of mapping that $futureEntity to a PersonEntity?
I tried to assign it manually, and then run my validations which seems to work, but i think this is cumbersome if a model has more than 30 fields...
Hint: Im on Symfony 4.4
Thank you!
Doc: How to process forms in Symfony
You need to install the Form bundle: composer require symfony/form (or composer require form if you have the Flex bundle installed)
Create a new App\Form\PersonType class to set the fields of your form and more: doc
In App\Controller\PersonController, when you instanciate the Form, just pass PersonType::class as a first parameter, and an new empty Person entity as a second one (the Form bundle will take care of the rest):
$person = new Person();
$form = $this->createForm(PersonType::class, $person);
The whole controller code:
<?php
namespace App\Controller;
use App\Entity\Person;
use App\Form\PersonType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class PersonController extends AbstractController
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager) {
$this->entityManager = $entityManager;
}
/**
* #Route("/person/new", name="person_new")
*/
public function new(Request $request): Response
{
$person = new Person(); // <- new empty entity
$form = $this->createForm(PersonType::class, $person);
// handle request (check if the form has been submited and is valid)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$person = $form->getData(); // <- fill the entity with the form data
// persist entity
$this->entityManager->persist($person);
$this->entityManager->flush();
// (optional) success notification
$this->addFlash('success', 'New person saved!');
// (optional) redirect
return $this->redirectToRoute('person_success');
}
return $this->renderForm('person/new.html.twig', [
'personForm' => $form->createView(),
]);
}
}
The minimum to display your form in templates/person/new.html.twig: just add {{ form(personForm) }} where you want.
Related
What is the right way to pass more variables to FOSUserBundle settings twig template (Profile/show_content.html.twig) in Symfony 3.4?
I basically want to rewrite showAction() method and pass more than user variable ti twig template.
I tried to following this tutorial. It seems it does no longer work with Symfony 3.4
The way I do it (and there might be better methods) is simply create a new controller with a route to the original 'show route', together with the variables I want to pass. Here is an example of the showAction() with an extra variable rendered_address:
namespace App\Controller;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class ProfileController extends Controller
{
/**
* Show the user.
* #Route("/profile/show")
*/
public function showAction()
{
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
$address = $this->getUser()->renderAddress(); // here is get my variable
return $this->render('#FOSUser/Profile/show.html.twig', array(
'user' => $user,
'rendered_address' => $address // here is pass my variable
));
}
}
I'm using FOSUserBundle on Symfony2.
I extended the User class to have additional fields, therefore I also added the new fields in the twigs.
One of those fields is a licence code. When a user fills in that field I want to perform a connection to DB to look if that license is valid. If not returns an error, if yes creates an event in the "licenceEvents" table assigning the current user to that license.
[EDIT] As suggested I created a custom validator (which works like a charm), and I'm now struggling with the persisting something on DB once the user is created or updated.
I created an event listener as follows:
<?php
// src/AppBundle/EventListener/UpdateOrCreateProfileSuccessListener.php
namespace AppBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Doctrine\ORM\EntityManager; //added
class UpdateOrCreateProfileSuccessListener implements EventSubscriberInterface
{
private $router;
public function __construct(UrlGeneratorInterface $router, EntityManager $em)
{
$this->router = $router;
$this->em = $em; //added
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_COMPLETED => array('onUserCreatedorUpdated',-10),
FOSUserEvents::PROFILE_EDIT_COMPLETED => array('onUserCreatedorUpdated',-10),
);
}
public function onUserCreatedorUpdated(FilterUserResponseEvent $event)
{
$user = $event->getUser();
$code = $user->getLicense();
$em = $this->em;
$lastEvent = $em->getRepository('AppBundle:LicenseEvent')->getLastEvent($code);
$licenseEvent = new LicenseEvent();
// here I set all the fields accordingly, persist and flush
$url = $this->router->generate('fos_user_profile_show');
$event->setResponse(new RedirectResponse($url));
}
}
My service is like follows:
my_user.UpdateOrCreateProfileSuccess_Listener:
class: AppBundle\EventListener\UpdateOrCreateProfileSuccessListener
arguments: [#router, #doctrine.orm.entity_manager]
tags:
- { name: kernel.event_subscriber }
The listener is properly triggered, manages to create the connection to DB as expected, but gives me the following error
Catchable Fatal Error: Argument 1 passed to AppBundle\EventListener\UpdateOrCreateProfileSuccessListener::onUserCreatedorUpdated()
must be an instance of AppBundle\EventListener\FilterUserResponseEvent,
instance of FOS\UserBundle\Event\FilterUserResponseEvent given
I must be missing something very stupid...
Another question is: I don't want to change the redirect page, so that if the original page was the "email sent" (after a new user is created) let's go there, otherwise if it's a profile update show the profile page.
I am using the FOSRestBundle to create a REST application but since REST features is only a part, I am also using some of Symfony2 built-in automation tools to generate my CRUD code. Everything works fine but I am unable to correctly map the route and I will appreciate some insight and example on how to do this manually. I have read the manual route definition in the FOS manual stating to use the given annotations but how do I do this since the CRUD code created by Symfony2 uses a different annotation?
Here is an example:
class UserController extends Controller
{
/**
* Lists all User entities.
*
* #Route("/", name="user")
* #Method("GET")
* #Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CompanyWebServicesBundle:User')->findAll();
return array(
'entities' => $entities,
);
}
FOSRest manual gives the annotation for GET as
use FOS\RestBundle\Controller\Annotations\Get;
/**
* GET Route annotation.
* #Get("/likes/{type}/{typeId}")
*/
When I use the route as /index, it gives me an error and my route definition in config.yml is:
index:
type: rest
resource: Company\WebservicesBundle\Controller\UserController
How can I fix this problem?
If I were you, I would create separate bundles for your REST controllers and your generic CRUD controllers. This will make things easier to maintain (in my opinion). For example, create a AcmeMainBundle and a AcmeRestBundle, and then create a separate class to actually perform the actions that you will call from both bundles. Something like this:
// src/Acme/MainBundle/Crud/User.php (create this folder structure)
class User{
private $em;
public function __construct($em){
$this->em = $em;
}
public function getUser($id){
return $this->em->getRepository('AcmeMainBundle:User')->find($id);
}
}
Then:
// src/Acme/MainBundle/Controller/UserController.php
use Symfony\Component\HttpFoundation\Request;
use Acme\MainBundle\Crud\User;
class UserController extends Controller {
public function getAction($request){
$em = $this->getDoctrine()->getManager();
$getUser = new User($em);
$user = $getUser ->getUser($request->query->get('user_id'));
// return response
}
}
And then:
// src/Acme/RestBundle/Controller/UserController.php
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Routing\ClassResourceInterface;
use Symfony\Component\HttpFoundation\Request;
class UserController extends Controller implements ClassResourceInterface {
/**
* #Rest\View()
*/
public function getAction($id){
$em = $this->getDoctrine()->getManager();
$getUser = new User($em);
$user = $getUser ->getUser($id);
// return using the default format defined in config.yml
return array(
"success"=>'true',
"user" => $user
);
} // get_user [GET] /users/{id}
}
Please note that using the ClassResourceInterface means your method names will be used to generate the routes. see FOSRestBundle Docs for more info on that.
You can do something similar to this for all your CRUD, that way you keep your routes separate and maintainable, but still have a single code base to update.
With propel we have findOneOrCreate()
Example.
$bookTag = BookTagQuery::create()
->filterByBook($book)
->findOneOrCreate();
In doctrine anywhere in the controller We can do something like that.
...................
$filename='something';
$document_exists = $em->getRepository('DemoBundle:Document')
->findOneBy(array('filename' => $filename));
if (null === $document_exists) {
$document = new Document();
$document->setFilename($filename);
$em->persist($document);
$em->flush();
}
Is there another way to achieve this in Doctrine?
Is it OK to call the Entity Manager inside the Entity Repository?
Any suggestions?
Easiest way is to extend the base repository:
// src/Acme/YourBundle/Entity/YourRepository.php
namespace Acme\YourBundle\Entity;
use Doctrine\ORM\EntityRepository;
class YourRepository extends EntityRepository
{
public function findOneOrCreate(array $criteria)
{
$entity = $this->findOneBy($criteria);
if (null === $entity)
{
$entity = new $this->getClassName();
$entity->setTheDataSomehow($criteria);
$this->_em->persist($entity);
$this->_em->flush();
}
return $entity
}
}
Then tell your entity to use this repository or extend in even further for specific entities:
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Acme\YourBundle\Entity\YourRepository")
*/
class Product
{
//...
}
and use it in your controller:
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AcmeStoreBundle:Product')
->findOrCreate(array('foo' => 'Bar'));
Source: http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes
Just be aware of that flush inside the repository as it would flush all unsaved changes in the EntityManager this way.
Have a look at the constructor of Doctrine\ORM\Repository here.
The EntityManager is mandatory for constructing a repository. The manager can - by default - not be accessed directly from a repository object because the property _em and the getter function getEntityManager are protected.
but ... yes, sure it is "OK" to call the EntityManager via the _em property inside a repository. All the other methods like findBy, ... etc. use it aswell and need the entity-manager to work actually :)
/**
* #var EntityManager
*/
protected $_em;
public function __construct($em, Mapping\ClassMetadata $class)
{
$this->_entityName = $class->name;
$this->_em = $em;
$this->_class = $class;
}
/**
* #return EntityManager
*/
protected function getEntityManager()
{
return $this->_em;
}
You can easily add a findOneOrCreate method to your entity repository or create a generic extended repository including that method.
then you can extend this new base repository whenever you need the method in a concrete entity repository.
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();)