Symfony-ux LiveComponent - symfony

I wanted to try Symfony-ux LIveComponent for a project, a small classic collectionType form with a delete button for each card, an add button to add cards as you go, fill in the generate fields and save everything with a save button.
I used a lot of help from symfony documentation, everything works except data saving, it's a bit annoying, the example I'm going to put below uses symfony documentation but it's not taken into account, maybe I forgot something, but in the current state of things I'm a bit lost.
If someone who has already used the LIve Component of symfony and had this problem would have some time, or just someone available for help it would be great :)
Note that I have no error, the ajax that is triggered is correct.
Here is the creation of my form and its save function:
public Campaign $campaign;
protected function instantiateForm(): FormInterface
{
return $this->createForm(ListMissionFormType::class, $this->campaign);
}
#[LiveAction]
public function save(Request $request, EntityManagerInterface $entityManager): Response
{
$this->submitForm();
$this->validate();
$mission = $this->getFormInstance()->getData();
$entityManager->persist($mission);
$entityManager->flush();
$this->addFlash('success', 'Modification enregistré');
return $this->redirect($request->headers->get('referer'), Response::HTTP_SEE_OTHER);
}

Related

Symfony login form redirect according to user role

I've used the maker bundle to create a standard login form. When the user has successfully logged in it calls function onAuthenticationSuccess to redirect to the new page.
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse($this->urlGenerator->generate('app_homepage'));
}
However, I would like to redirect to different pages depending on what role the user has. I would like to do something like:
if ($this->security->isGranted('ROLE_STANDARD_USER')) {
return new RedirectResponse($this->urlGenerator->generate('app_homepage'));
}
if ($this->security->isGranted('ROLE_SYS_ADMIN')) {
return new RedirectResponse($this->urlGenerator->generate('app_ADMINpage'));
}
But the error I'm getting is Undefined property: App\Security\LoginFormAuthenticator::$security
Many thanks in advance for the help.
I didn't have much to go by, but it seems that the default implementation of the LoginFormAuthenticator does not request the AuthorizationChecker.
You can fix this by injecting an AuthorizationChecker into your class using the constructor. Example of how dependency injection works
Seeing as you have used the maker bundle, it is safe to assume you have autowiring turned on for your services, meaning that the Symfony kernel will automagically do all the rest of the work

Akeneo 2.2.8 : How can I get the original attribute data in the akeneo.storage.pre_save event?

I'm using Akeneo 2.2.8 and I'm trying to use the akeneo.storage.pre_save-event to compare the original product data with the new data provided. I do this by subscribing to the akeneo.storage.pre_save-event:
In event_subscribers.yml:
parameters:
vendor.bundle.event_subscriber.product_save.class: Vendor\Bundle\CustomBundle\EventSubscriber\ProductSaveSubscriber
services:
vendor.bundle.event_subscriber.product_save:
class: '%vendor.bundle.event_subscriber.product_save.class%'
arguments:
- '#pim_catalog.repository.product'
tags:
- { name: kernel.event_listener, event: akeneo.storage.pre_save, method: onPreSave, priority: 255 }
In ProductSaveSubscriber.php:
/**
* #var ProductRepositoryInterface
*/
protected $productRepository;
public function __construct(ProductRepositoryInterface $productRepository)
{
$this->productRepository = $productRepository;
}
public function onPreSave(GenericEvent $event)
{
/** #var Product $subject */
$subject = $event->getSubject();
if ($subject instanceof Product) {
$originalProduct = $this->productRepository->findOneByIdentifier($subject->getIdentifier());
foreach ($subject->getAttributes() as $attribute) {
if ($attribute->getReadOnly()) {
echo "{$attribute->getCode()} : {$subject->getValue($attribute->getCode())}\n";
echo "{$attribute->getCode()} : {$originalProduct->getValue($attribute->getCode())}\n";
}
}
}
}
Now when I run this code, I expect the second echo-statement to give the original data (since I've loaded that anew). However, the original product I load from the repository also has the new data.
Another thing to note here is that if I add a die()-statement, the data is not stored in the database. So it seems that the repository returns the in-memory model or something like that.
Can anyone point me in the right direction? Or am I using the wrong approach to compare newly-entered data with already existing data?
Now when I run this code, I expect the second echo-statement to give
the original data (since I've loaded that anew). However, the original
product I load from the repository also has the new data.
This might be because the object is referenced in doctrine's unit of work. So when you use the repository to fetch what you think is the original object, might actually be the same object you've updated.
Another thing to note here is that if I add a die()-statement, the
data is not stored in the database. So it seems that the repository
returns the in-memory model or something like that.
That's because since your subscriber is listening on the PRE_SAVE event, the updated product has not been flushed in the database yet. Saving a product goes this way:
PRE_SAVE event thrown
COMMIT / FLUSH
POST_SAVE event thrown
So if you call die during the PRE_SAVE event, the COMMIT / FLUSH won't be called.
Can anyone point me in the right direction? Or am I using the wrong
approach to compare newly-entered data with already existing data?
I don't know your particular use case but you might want to use a Query function (see https://github.com/akeneo/pim-community-dev/blob/2.2/src/Pim/Bundle/CatalogBundle/Doctrine/ORM/Query/FindAttributesForFamily.php). It's purpose is to directly fetch in the database the data you need (it will be the original value since the product hasn't been flushed in DB on PRE_SAVE)
I hope this helps.

How to use the User value resolver in Symfony 3.2?

I'm building a small website using symfony 3.2.
There is a page in which the user can change its profile data with a form. I used the structure seen in the official tutorial. Here is the declaration of my controller :
/** #Route("/profil", name="profil_show_user") */
public function userProfileAction(Request $request, UserInterface $user) {
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw $this->createAccessDeniedException();
}
//...
$user->getProfile();//and do stuff
//...
}
My problem is that if the user is disconnected for being inactive for too long, or if someone bookmark the page but is not connected, I have this ugly error coming :
Controller
"AppBundle\Controller\ProfilController::userProfileAction()" requires
that you provide a value for the "$user" argument. Either the argument
is nullable and no null value has been provided, no default value has
been provided or because there is a non optional argument after this
one. 500 Internal Server Error - RuntimeException
In the New in Symfony 3.2 changelog, there is something about the new User value resolver. I tried to change to UserInterface $user = null, and it make the page redirect to the path I set in failure_path of security.yml, which is the good behaviour.
But then if I'm connected and go to profil_show_user, I get that other error :
Error: Call to a member function getProfile() on null
I search thoroughly the symfony documentation but couldn't find anything.
Could someone explain to me what goes wrong, what I misunderstood and how can I make this work ?
EDIT :
I thought I might say that if I don't use te value resolver, everything works fine. This is an educationnal and curiosity question about a new feature which I don't manage to use. This code works :
/** #Route("/profil", name="profil_show_user") */
public function userProfileAction(Request $request) {
$user = $this->getUser();
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw $this->createAccessDeniedException();
}
//...
$user->getProfile();//and do stuff
//...
}

Doctrine event subscribers - performance and convention

I've got a basic question regarding the right way to do things in Symfony2, with specific emphasis on Doctrine event subscribers. I know how to implement them, but something has been bugging me. Currently, I have the following class.
namespace MyProject\MainBundle\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use MyBundle\MainBundle\Entity\LandingPageAggregator;
class LandingPageAggregatorSubscriber implements EventSubscriber {
/**
* Returns an array of events this subscriber wants to listen to.
*
* #return array
*/
public function getSubscribedEvents() {
return array(
'prePersist',
'preUpdate',
);
}
public function prePersist(LifecycleEventArgs $args) {
$entity = $args->getObject();
if (!$entity instanceof LandingPageAggregator)
return;
// Adittional stuff here...
}
}
I got this from this Symfony article, and it's working fine, my question is just the following:
Is there a better way to do this? Is this actually the accepted standard way of setting stuff like "Posted By" or "Created Date" fields?
Is this not performance intensive? Forgive me if I'm wrong, but registering 100 of these, doesn't that mean that for every single persist to the database, Symfony has to run thorugh all 100 subscribers and call the prePersist method on each of them? This seems like a giant waste of resources, thus the purpose of this question.
If I'm correct with number 2. above here, is there any better/less intensive method of doing the same thing? I just read on doctrine's documentation that they've introduced a new annotation as of 2.4, but I'm not using that version yet. Will that solve this issue in any case?
As a side question, what is the difference between the listener and subscriber as stated in the Symfony documentation linked above?
Thanks for any advice here!

Right form events to display modified data and update modified data?

A simple task: before displaying the form, if $data->getRole() starts with "ROLE_", remove this string and display only the rest. When user submit the form, do the opposite: add "ROLE_" before the name.
What's the best place to do this? Actually i'm using PRE_SET_DATA and POST_BIND. Are these the right events to perform this operation?
$builder->addEventListener(FormEvents::PRE_SET_DATA,
function(DataEvent $event){
if(is_null($data = $event->getData()) || !$data->getId()) return;
$data->setRole(strtoupper(preg_replace('/^ROLE_/i', '',
$data->getRole())));
});
$builder->addEventListener(FormEvents::POST_BIND,
function(DataEvent $event) {
if(is_null($data = $event->getData()) || !$data->getId()) return;
$data->setRole('ROLE_' . strtoupper($data->getRole()));
});
Well reading the role without the prefix "ROLE" is not something I would do using events. As they obsfusicate your workflow, events should be used with care! Working with symfony for some time, I used them once or twice when there was really no other way. All the other times there was a better way.
I would tend to simply add a function getShortRole and setShortRole and use shortRole within your Entity:
class MyEntity {
private $role;
public function setShortRole($role) {
$this->role = 'ROLE_' . strtoupper($role);
}
public function getShortRole() {
return strtoupper(preg_replace('/^ROLE_/i', '', $this->role));
}
}
You are saving yourself a lot of trouble working with models instead of events!
A second, more complicated way would be to use a Model which represents the form instead of the Entity and maps the form to the entity. Here is a good article about this here!
I use it myself and it works nice.

Resources