I have three entities: User, Store and Category.
User has a bidirectional relation with Store and store has a bidirectional relation with Category also.
Each user can create many stores and he can create many categories for each one.
I have managed to secure the store using Voters and user can access only to his stores.
This is the route of store
dashboard_store_view:
path: /{id}/view
defaults: { _controller: ProjectStoreBundle:StoreDashboard:view }
The url is like this
http://localhost/project/web/app_dev.php/dashboard/store/1/view
This is the controller StoreDashboardController.php
<?php
//..................
public function viewAction(Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('view', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$em = $this->getDoctrine()->getManager();
$store = $em->getRepository('ProjectStoreBundle:Store')->findOneById($store);
return $this->render('ProjectDashboardBundle:Store:view.html.twig',
array(
'store' => $store
));
}
And this is the StoreVoter
<?php
namespace Project\StoreBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class StoreVoter implements VoterInterface
{
const VIEW = 'view';
const EDIT = 'edit';
const DELETE = 'delete';
public function supportsAttribute($attribute)
{
return in_array($attribute, array(
self::VIEW,
self::EDIT,
self::DELETE,
));
}
public function supportsClass($class)
{
$supportedClass = 'Project\StoreBundle\Entity\Store';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
/**
* #var \Project\StoreBundle\Entity\Store $store
*/
public function vote(TokenInterface $token, $store, array $attributes)
{
// check if class of this object is supported by this voter
if (!$this->supportsClass(get_class($store))) {
return VoterInterface::ACCESS_ABSTAIN;
}
// check if the voter is used correct, only allow one attribute
// this isn't a requirement, it's just one easy way for you to
// design your voter
if(1 !== count($attributes)) {
throw new InvalidArgumentException(
'Only one attribute is allowed for VIEW or EDIT'
);
}
// set the attribute to check against
$attribute = $attributes[0];
// get current logged in user
$user = $token->getUser();
// check if the given attribute is covered by this voter
if (!$this->supportsAttribute($attribute)) {
return VoterInterface::ACCESS_ABSTAIN;
}
// make sure there is a user object (i.e. that the user is logged in)
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_DENIED;
}
switch($attribute) {
case 'view':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $store->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'edit':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $store->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'delete':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $store->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
}
}
}
I tried to do the same thing with categories but I failed to secure each category to his own store and so evry user can edit any category
This is the route
dashboard_category_edit:
pattern: /{store_id}/edit/{id}
defaults: { _controller: ProjectStoreBundle:CategoryDashboard:edit }
The url is like this
http://localhost/project/web/app_dev.php/dashboard/categories/store/1/edit/3
CategoryDashboardController.php
public function editAction(Category $category, Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$form = $this->createForm(new CategoryEditType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Category bien modifié');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:edit.html.twig',
array(
'form' => $form->createView() ,
'store' => $store
));
}
and this is the CategoryVoter
<?php
namespace Project\StoreBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class CategoryVoter implements VoterInterface
{
const VIEW = 'view';
const EDIT = 'edit';
const DELETE = 'delete';
public function supportsAttribute($attribute)
{
return in_array($attribute, array(
self::VIEW,
self::EDIT,
self::DELETE,
));
}
public function supportsClass($class)
{
$supportedClass = 'Project\StoreBundle\Entity\Category';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
/**
* #var \Project\StoreBundle\Entity\Category $category
*/
public function vote(TokenInterface $token, $category, array $attributes)
{
// check if class of this object is supported by this voter
if (!$this->supportsClass(get_class($category))) {
return VoterInterface::ACCESS_ABSTAIN;
}
// check if the voter is used correct, only allow one attribute
// this isn't a requirement, it's just one easy way for you to
// design your voter
if(1 !== count($attributes)) {
throw new InvalidArgumentException(
'Only one attribute is allowed for VIEW or EDIT'
);
}
// set the attribute to check against
$attribute = $attributes[0];
// get current logged in user
$user = $token->getUser();
// check if the given attribute is covered by this voter
if (!$this->supportsAttribute($attribute)) {
return VoterInterface::ACCESS_ABSTAIN;
}
// make sure there is a user object (i.e. that the user is logged in)
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_DENIED;
}
switch($attribute) {
case 'view':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $category->getStore()->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'edit':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $category->getStore()->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'delete':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $category->getStore()->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
}
}
}
The problem is that categories is not realted to user but it is related to store, so how can I secure it ?
I find this solution doing verification if $category->getStore <> $store so throw AccessDeniedException without using Voters and it work fine now.
if ($category->getStore() <> $store) {
throw new AccessDeniedException('Unauthorised access!');
}
So the controller will be like this
/**
* #ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function editAction(Category $category, Store $store)
{
if ($category->getStore() <> $store) {
throw new AccessDeniedException('Unauthorised access!');
}
$form = $this->createForm(new CategoryEditType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Category bien modifié');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:edit.html.twig',
array(
'form' => $form->createView() ,
'store' => $store
));
}
Is it a good solution ?
If each Category has only one Store ther is no point in using store_id in route when you want to edit Category. Just use category_id and get $store from $category by calling $store = $category->getStore();. Change editAction:
/**
* #ParamConverter("category", options={"mapping": {"category_id":"id"}})
*/
public function editAction(Category $category)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $category)) {
throw new AccessDeniedException('Unauthorised access!');
}
$store = $category->getStore();
(...)
I find this solution that getting the ID of store in table category then doing two verifications,
if id_store in table category doesn't match Store's owner and if id_store in table category doesn't match current store
/**
* #ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function editAction(Category $category, Store $store)
{
// get id_store in table category
$idStore = $category->getStore();
// if id_store in table category doesn't match user
if (false === $this->get('security.context')->isGranted('edit', $idStore)) {
throw new AccessDeniedException('Unauthorised access!');
}
// if id_store in table category doesn't match current store
if (false === ($idStore === $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$form = $this->createForm(new CategoryEditType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Category bien modifié');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:edit.html.twig',
array(
'form' => $form->createView() ,
'store' => $store
));
}
Related
This question already has answers here:
Symfony2 Form Entity Update
(3 answers)
Closed 2 years ago.
I'm making a REST API with Symfony 4.4. The API largely revolves around putting data into a database, using Doctrine. I have figured out how to add rows to the database, but now I'm stuck on changing data. I know how I can take a row from the database and that, in theory, I can change fields by calling the setter of a property, but right now, I seem to be getting an array instead of the desired entity and, seemingly more difficult, I want to be able to dynamically change the properties of the existing row, so that I don't have to include every field of the object of the row I'm changing and call every setter.
Here is my code:
// PersonController.php
/**
* #IsGranted("ROLE_USER")
* #Rest\Post("/addperson")
* #param Request $request
* #return Response
*/
public function addOrUpdatePerson(Request $request)
{
$data = json_decode($request->getContent(), true);
$em = $this->getDoctrine()->getManager();
$person = new Person();
$form = $this->createForm(PersonType::class, $person);
$form->submit($data);
if (!$form->isSubmitted() || !$form->isValid())
{
return $this->handleView($this->view($form->getErrors()));
}
if (isset($data['id']))
{
// This person exists, change the row
// What to do?
}
// This person is new, insert a new row
$em->persist($person);
$em->flush();
return $this->handleView($this->view(['status' => 'ok'], Response::HTTP_CREATED));
}
// PersonType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', IntegerType::class, ['mapped' => false])
->add('inits')
->add('firstname')
->add('lastname')
->add('email')
->add('dateofbirth', DateTimeType::class, [
'widget' => 'single_text',
// this is actually the default format for single_text
'format' => 'yyyy-MM-dd',
])
// Some other stuff
->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Person::class,
'csrf_protection' => false
));
}
I doubt the Person entity is relevant here, but if it is, please let me know and I'll include it ASAP!
As a response to the suggestion of the other question from Symfony 2; it doesn't seem to fix my problem (entirely). As a result of this question, I have changed my function to this (which doesn't work, but doesn't throw any errors):
public function addOrUpdatePerson(Request $request)
{
$data = json_decode($request->getContent(), true);
$em = $this->getDoctrine()->getManager();
if (isset($data['id'])) {
// This person exists
$existing = $em->getRepository(Person::class)->find(['id' => $data['id']]);
$this->getDoctrine()->getManager()->flush();
$form = $this->createForm(PersonType::class, $existing);
$form->handleRequest($request);
// this doesn't seem to do anything
// $em->persist($existing);
$em->flush();
return $this->handleView($this->view($existing));
}
}
I think I'm still missing some info, like what to do at // perform some action, such as save the object to the database. I also notice a lot has changed since Symfony 2, and as a result it is not obvious to me what I should do.
After '$person = new Person()' juste add :
If (isset($data['id']) && 0 < $data['id']) {
$person=$em->getRepository(Person::class)->find($data['id']);
}
If (!$person) {
Throw new \Exception('Person not found');
}
1.) You don't have to use json_decode directly. You can use the following code instead:
// Person controller
/**
* #Route("/person", name="api.person.add", methods={"POST"})
* #Security("is_granted('ROLE_USER')")
*/
public function addPerson(Request $request)
{
$person = new Person();
$form = $this->createForm(PersonType::class, $person);
$form->submit($request->request->all());
if (!$form->isSubmitted() || !$form->isValid()) {
throw new \Exception((string) $form->getErrors(true));
}
$em = $this->getDoctrine()->getManager();
$em->persist($person);
$em->flush();
...
}
2.) When you're updating entity you need to load it first and skip the $em->persist($entity); part. In this case, we provide the ID of the entity via the path variable (there are various ways to provide it but this one is quite common). NOTE: I've set $id parameter as mixed because it can be integer or string if you're using UUID type of IDs.
// Person controller
/**
* #Route("/person/{id}", name=api.person.patch", methods={"PATCH"})
* #Security("is_granted('ROLE_USER')")
*/
public function patchPerson(Request $request, mixed $id)
{
// Load person
$personRepository = $this->getDoctrine()->getRepository(Person::class);
$person = $personRepository->find($id);
if (!$person) { throw new \Exception('Entity not found'); }
$form = $this->createForm(PersonType::class, $person);
$form->submit($request->request->all());
if (!$form->isSubmitted() || !$form->isValid()) {
throw new \Exception((string) $form->getErrors(true));
}
$em = $this->getDoctrine()->getManager();
$em->flush();
...
}
3.) In general usage, we don't set the ID property via posted data (unless it is required). We rather use generated value instead. When you insert new entity you gen use its ID to address it for modifications. Sample:
<?php
namespace App\Entity;
use Ramsey\Uuid\Uuid;
use Doctrine\ORM\Mapping as ORM;
class Person
{
/**
* #var Uuid
*
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
* #Groups({"public"})
*/
protected $id;
// Other entity properties ...
public function getId(): ?string
{
return $this->id;
}
public function setId(string $id): self
{
$this->id = $id;
return $this;
}
// Setters and getters for other entity properties ...
}
4.) Entity class in FormType (PersonType.php) is very relevant. After form submission and validation you access properties of the entity itself within the controller - not the decoded payload data from the request directly. Symfony's form system will make sure that the input data is valid and matches the requirements and constraints set in the entity model or form type specification.
// Person controller
/**
* #Route("/person", name="api.person.add", methods={"POST"})
* #Security("is_granted('ROLE_USER')")
*/
public function addPerson(Request $request)
{
$person = new Person();
$form = $this->createForm(PersonType::class, $person);
$form->submit($request->request->all());
if (!$form->isSubmitted() || !$form->isValid()) {
throw new \Exception((string) $form->getErrors(true));
}
$em = $this->getDoctrine()->getManager();
$em->persist($person);
$em->flush();
$id = $person->getId();
$firstName = $person->getFirstname();
$lastName = $person->getLastname();
// etc
...
}
5.) If you want to use the same method/endpoint for adding and updating entity you can do something like #lasouze mentioned.
// Person controller
/**
* #Route("/person", name=api.person.add_or_update", methods={"POST", "PATCH"})
* #Security("is_granted('ROLE_USER')")
*/
public function patchPerson(Request $request)
{
$id = $request->request->get('id', null);
if (!$id) {
$person = new Person();
} else {
// Load person
$personRepository = $this->getDoctrine()->getRepository(Person::class);
$person = $personRepository->find($id);
if (!$person) { throw new \Exception('Entity not found'); }
}
$form = $this->createForm(PersonType::class, $person);
$form->submit($request->request->all());
if (!$form->isSubmitted() || !$form->isValid()) {
throw new \Exception((string) $form->getErrors(true));
}
$em = $this->getDoctrine()->getManager();
$em->flush();
...
}
PS: $form->submit($request->request->all()); will not work for file uploads because $request->request->all() does not contain parameters provided by $_FILES. In my case I ended up merging data like $form->submit(array_merge($request->request->all(), $request->files->all())); but this is probably not the best solution. I'll update my answer if I'll figure out anything better.
I would like to know how to implement a check for a field inside voters of an entity.
I have for example my entity Post where I want that a user not admin can't edit title field. Only admin can edit this field.
So I have created my voters but I don't know how to create this check because inside $post there is the old post entity and I don't know how to implement the check for title field
This is my easy voters file
class PostVoter extends Voter
{
const VIEW = 'view';
const EDIT = 'edit';
private $decisionManager;
public function __construct(AccessDecisionManagerInterface $decisionManager)
{
$this->decisionManager = $decisionManager;
}
protected function supports($attribute, $subject)
{
if (!in_array($attribute, array(self::VIEW, self::EDIT))) {
return false;
}
if (!$subject instanceof Post) {
return false;
}
return true;
}
protected function voteOnAttribute(
$attribute,
$subject,
TokenInterface $token
) {
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
if ($this->decisionManager->decide($token, array('ROLE_SUPER_ADMIN'))) {
return true;
}
/** #var Post $post */
$post = $subject;
switch ($attribute) {
case self::VIEW:
return $this->canView($post, $user);
case self::EDIT:
return $this->canEdit($post, $user);
}
throw new \LogicException('This code should not be reached!');
}
private function canView(Post $post, User $user)
{
if ($this->canEdit($post, $user)) {
return true;
}
return true;
}
private function canEdit(Post $post, User $user)
{
return $user === $post->getUser();
}
}
I would like to implement inside canEdit a check for the title field.
I have tried to print $post but there is only old value not some information for new value.
Couple of possible approaches.
The one I would use is to add a 'edit_title' permission to the voter then adjust my form to make the title read only if the edit_title permission was denied. This not only eliminates the need to check for a changed title but also makes things a bit friendlier for the users. One might imagine them being a bit frustrated with a form that allows them to change the title but then the app rejects the change.
If you really wanted to detect a title change then you could adjust the setTitle method in your post entity. Something like:
class Post {
private $titleWasChanged = false;
public function setTitle($title) {
if ($title !== $this->title) $this->titleWasChanged = true;
$this->title = $title;
And then of course check $titleWasChanged from the voter.
If you really wanted to go all out, the Doctrine entity manager actually has some change checking capability. You could probably access it via the voter but that would probably be overkill. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html
I use Symfony 3.
I have an entity (Book) that I can create and edit. So I have created two actions and forms to do this. But, I want to save historical of this change.
I have imagine a solution. Two Entity, Book and SubBook to inherit of BaseBook. SubBook is the history of all edit of Book and is linked by a field "parent".
public function editAction(Request $request, ...)
{
$book = ...
$form = $this->createForm(BookType::class, $book);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$subbook = new SubBook($book);
$em = $this->getDoctrine()->getManager();
$em->persist($subbook);
$em->flush();
// ...
}
// ...
}
AppBundle/Entity/Book
class Book extends BaseBook {
// ...
}
AppBundle/Entity/SubBook
class SubBook extends BaseBook {
// ...
public function __construct($book)
{
parent::__construct();
// ...
$this->parent = $book;
}
}
AppBundle/Model/BaseBook
abstract class BaseBook
{
// ...
}
But my problem is that when I submit the edit form, my Book ($book) is automatically persist when I execute $em->flush(); so it is updated even if I don't execute $em->persist($book);.
So, do you have any idea to do this ? or to solve this error ?
Thanks !
You should use $em->detach($book);. You can find more information in the documentation
So, I finally did this. My Book is the entity with last update and I save in an SubBook entity all values that have changed.
My SubBook entity is so almost like Book but all value (not changed) can be null. So I abandoned the model.
public function editAction(Request $request, $id)
{
$book = ...
$form = ...
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$uow = $em->getUnitOfWork();
$uow->computeChangeSets();
$changeset = $uow->getEntityChangeSet($book);
$subbook = new SubBook($book, $changeset);
$em->persist($subbook);
$em->persist($book);
$em->flush();
// ...
}
// ...
}
AppBundle/Entity/SubBook
public function __construct($book, $changeset)
{
// ...
$this->parent = $book;
foreach ($changeset as $key => $values) {
$this->$key = $values[0]; // values[0] => Hold value, [1] => New value
}
}
I have a simple registration form which has 3 fields. Email, name and password. So far so good. I'm able to create new users. The problem comes when I want to edit user information.
I want to update password field in the database only if html password field is not empty
public function editAction(User $user, Request $request)
{
$form = $this->createForm(new UserForm(), $user);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->redirectToRoute('bd_user_list');
}
return $this->render('BDUserBundle:User:add.html.twig', [
'form' => $form->createView()
]);
}
If I left password field blank I get this error (which is normal)
An exception occurred while executing 'UPDATE users SET password = ? WHERE id = ?' with params [null, 5]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null
I've read about validation groups but I'm not sure they can help.
// the solution
UserForm.php
[...]
builder->addEventSubscriber(new UserFormListener());
[...]
UserFormListener.php
<?php
namespace SDUserBundle\Form\EventListener;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class UserFormListener implements EventSubscriberInterface
{
private $password;
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::POST_SUBMIT => 'postSubmit'
);
}
public function preSetData(FormEvent $event)
{
$this->password = $event->getData()->getPassword();
}
public function postSubmit(FormEvent $event)
{
$data = $event->getData();
if ($data->getPassword() == false) {
$data->setPassword($this->password);
}
}
}
Fast solution
Modify your code as follows
public function editAction(User $user, Request $request)
{
$old_pwd = $user->getPassword(); //or whatever the method is called
$form = $this->createForm(new UserForm(), $user);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
if (null == $user->getPassword()) {
$user->setPassword($old_pwd);
}
$em->persist($user);
$em->flush();
$this->redirectToRoute('bd_user_list');
}
return $this->render('BDUserBundle:User:add.html.twig', [
'form' => $form->createView()
]);
}
More elegant solution
This is a first solution but involves some logic inside controller; maybe you need that code elsewhere, so you could migrate it into form events like FormEvents::PRE_SET_DATA
So you need to modify your UserForm as follows
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
[...]
class UserForm extends AbstractType
{
private $old_pwd;
[...]
$builder
[...]
->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$data = $event->getData();
$this->old_pwd = $data->getPassword();
})
->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
$data = $event->getData();
if (false == $data->getPassword()) {
$data->setPassword($this->old_pwd);
$this->setData($data);
}
})
}
I really don't know if second approach is a working one as I can't test it at the moment but FormEvents should help you accomplish what you need.
Another approach, not so good?
Maybe you could modify User setter directly
class User
{
[...]
public function setPassword($pwd)
{
if ($pwd) {
$this->pwd = //logic here to store a safe pwd
}
}
}
Why this third solution is the worst at all I let you to find yourself ;)
Side note
Just to let you know that isValid() take care for you about submitted controls from Symfony2.3 on so you don't need isSubmitted() control explicitly
I want to change the templating of the registration form in my project by extending the new twig layout. However it does not change. It doesnt show any errors, but I am still getting the original view of the form. I did everything I found in documentation, but it still wont change, why?
1) I extended the userBundle.
2) I made created the ApplicationSonataUserBundle and did this:
class ApplicationSonataUserBundle extends Bundle
{
/**
* {#inheritdoc}
*/
public function getParent()
{
return 'SonataUserBundle';
}
}
I made my new controller and overwrited the old one(I only changed the rendered layout):
<?php
namespace Application\Sonata\UserBundle\Controller;
use Sonata\UserBundle\Controller\RegistrationFOSUser1Controller as BaseController;
class Registration1Controller extends BaseController
{
public function registerAction()
{
$user = $this->container->get('security.context')->getToken()->getUser();
if ($user instanceof UserInterface) {
$this->container->get('session')->getFlashBag()->set('sonata_user_error', 'sonata_user_already_authenticated');
$url = $this->container->get('router')->generate('sonata_user_profile_show');
return new RedirectResponse($url);
}
$form = $this->container->get('sonata.user.registration.form');
$formHandler = $this->container->get('sonata.user.registration.form.handler');
$confirmationEnabled = $this->container->getParameter('fos_user.registration.confirmation.enabled');
$process = $formHandler->process($confirmationEnabled);
if ($process) {
$user = $form->getData();
$authUser = false;
if ($confirmationEnabled) {
$this->container->get('session')->set('fos_user_send_confirmation_email/email', $user->getEmail());
$url = $this->container->get('router')->generate('fos_user_registration_check_email');
} else {
$authUser = true;
$route = $this->container->get('session')->get('sonata_basket_delivery_redirect');
if (null !== $route) {
$this->container->get('session')->remove('sonata_basket_delivery_redirect');
$url = $this->container->get('router')->generate($route);
} else {
$url = $this->container->get('session')->get('sonata_user_redirect_url');
}
}
$this->setFlash('fos_user_success', 'registration.flash.user_created');
$response = new RedirectResponse($url);
if ($authUser) {
$this->authenticateUser($user, $response);
}
return $response;
}
$this->container->get('session')->set('sonata_user_redirect_url', $this->container->get('request')->headers->get('referer'));
return $this->container->get('templating')->renderResponse('MpShopBundle:Frontend:registration.html.'.$this->getEngine(), array(
'form' => $form->createView(),
));
}
}
3) I added new Application\Sonata\UserBundle\ApplicationSonataUserBundle(), to the AppKernel.
Did I miss anything? What can be the problem?
UPDATE :
Now I am getting this error: Compile Error: Namespace declaration statement has to be the very first statement in the script. but my namespace is the first statement isnt it?