hi im trying to encode My password for My app user So i tried to encrypted in my setPassword function
unfortunately i get this error that i don't understand: Call to a member function encodePassword() on null error pic
<?php
namespace App\Entity;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Gedmo\Mapping\Annotation as Gedmo;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Admin
*#Vich\Uploadable
* #ORM\Table(name="admin")
* #ORM\Entity
*/
class Admin implements UserInterface
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* Undocumented variable
*
* #var UserPasswordEncoderInterface
*/
private $passwordEncoder ;
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$hash= $this->passwordEncoder->encodePassword($this,$password);
$this->password=$hash;
return $this ;
}
.......
whats wrong, and how can i fix it ! thnx
but this one work
<?php
namespace App\Controller\Admin;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserCrudController extends AbstractCrudController
{
/** #var UserPasswordEncoderInterface */
private $passwordEncoder;
public static function getEntityFqcn(): string
{
return User::class;
}
public function configureFields(string $pageName): iterable
{
return [
FormField::addPanel('Change password')->setIcon('fa fa-key'),
Field::new('plainPassword', 'New password')->onlyOnForms()
->setFormType(RepeatedType::class)
->setFormTypeOptions([
'type' => PasswordType::class,
'first_options' => ['label' => 'New password'],
'second_options' => ['label' => 'Repeat password'],
]),
];
}
public function createEditFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface
{
$formBuilder = parent::createEditFormBuilder($entityDto, $formOptions, $context);
$this->addEncodePasswordEventListener($formBuilder);
return $formBuilder;
}
public function createNewFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface
{
$formBuilder = parent::createNewFormBuilder($entityDto, $formOptions, $context);
$this->addEncodePasswordEventListener($formBuilder);
return $formBuilder;
}
/**
* #required
*/
public function setEncoder(UserPasswordEncoderInterface $passwordEncoder): void
{
$this->passwordEncoder = $passwordEncoder;
}
protected function addEncodePasswordEventListener(FormBuilderInterface $formBuilder)
{
$formBuilder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
/** #var User $user */
$user = $event->getData();
if ($user->getPlainPassword()) {
$user->setPassword($this->passwordEncoder->encodePassword($user, $user->getPlainPassword()));
}
});
}
}
i tried to add an Event listener so i create this class
<?php
namespace App\Controller\Admin;
use App\Entity\Admin;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use EasyCorp\Bundle\EasyAdminBundle\EventListener\AdminContextListener;
class AdminController extends AdminContextListener
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder ;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder=$encoder;
}
public static function getSetPasswordEvent()
{
return [
BeforeEntityPersistedEvent::class => ['setPassword'],
];
}
public function setPassword(BeforeEntityPersistedEvent $event)
{
$entity = $event->getEntityInstance();
if (!($entity instanceof Admin)) {
return;
}
$encoded = $this->encoder->encodePassword($entity, $entity->getPassword());
$entity->setPassword($encoded);
}
}
and it didn't work too
You should do that when User register, so add this before doing flush(); for new user:
$user = new UserEntity();
$user->setEmail($request->getEmail());
if ($request->getPassword())
{
$createUser->setPassword($this->encoder->encodePassword($user, $request->getPassword()));
}
$this->entityManager->persist($createUser);
$this->entityManager->flush();
$this->entityManager->clear();
Notice: $request containe payload comes form frontend {"email": "", "passwprd": ""}.
Notice: $createUser is a user object to flush.
Related
I'm developing my first Symfony project and so far pretty good except for the Login since the warning "invalid credentials" started popping up everytime I try to log in and don't really know why because I'm using AbstractLoginFormAuthenticator instead of AbstractFormLoginAuthenticator (the one I see the most), which is driving me a bit crazy because there is not much info.
User entity:
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
* #method string getUserIdentifier()
*/
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=false)
*/
private $name;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\OneToMany(targetEntity="Booking", mappedBy="user")
*/
private $userBooking;
public function getId(): ?int
{
return $this->id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name): void
{
$this->name = $name;
}
public function getEmail(): ?string
{
return $this->email;
}
/**
* #param mixed $email
*/
public function setEmail($email): void
{
$this->email = $email;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* #see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
Security controller:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// if ($this->getUser()) {
// return $this->redirectToRoute('target_path');
// }
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* #Route("/logout", name="app_logout")
*/
public function logout()
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}
LoginFormAuthenticator:
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function authenticate(Request $request): PassportInterface
{
$email = $request->request->get('email', '');
$request->getSession()->set(Security::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
// For example:
//return new RedirectResponse($this->urlGenerator->generate('some_route'));
//throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
return new RedirectResponse('home');
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}
Create User:
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserTypeUser;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class NewUserController extends AbstractController
{
#[Route('/newuser', name: 'new_user', methods: ['GET', 'POST'])]
public function index(Request $request): Response
{
$user = new User();
$form = $this->createForm(UserTypeUser::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->setRoles(['ROLE_USER']);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('home');
}
return $this->render('new_user/index.html.twig', [
'user' => $user,
'form' => $form->createView(),
]);
}
}
Thanks a lot in advance.
In your NewUserController you need to hash the password before you persist the user.
First you need to inject the service into the controller:
private $passwordHasher;
public function __construct(UserPasswordHasherInterface $passwordHasher)
{
$this->passwordHasher = $passwordHasher;
}
Then in your action you need to add one line inside the if condition:
if ($form->isSubmitted() && $form->isValid()) {
$user->setRoles(['ROLE_USER']);
$user->setPassword($this->passwordHasher->hashPassword($user, $user->getPassword());
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('home');
}
This assumes that you already set the password in your form, but it's not properly encoded yet. You can find this in the docs here: https://symfony.com/doc/current/security.html#c-hashing-passwords
Be careful, prior to Symfony 5.3 the password hasher was called password encoder and lived inside the security component. The code is fundamentally the same, but the class looks a bit different. See: https://symfony.com/doc/5.2/security.html#c-encoding-passwords
I am doing a crud for an entity in my application. The form have to upload a file, so i added the assert in the entity file. But when i submit my form the validator give me an error and said to me that the field image have to be a string. I've done upload of file a hundred times, that why i don't understand this time why the validator bundles react like that.
My entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\AlgorithmeRepository")
*/
class Algorithme
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $titre;
/**
* #ORM\Column(type="date")
*/
private $dateCreationArticle;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*
* #Assert\File(mimeTypes={ "image/jpeg","image/png"})
*/
private $image;
/**
* #ORM\Column(type="array")
*/
private $analyse = [];
public function getId(): ?int
{
return $this->id;
}
public function getTitre(): ?string
{
return $this->titre;
}
public function setTitre(string $titre): self
{
$this->titre = $titre;
return $this;
}
public function getDateCreationArticle(): ?\DateTimeInterface
{
return $this->dateCreationArticle;
}
public function setDateCreationArticle(\DateTimeInterface $dateCreationArticle): self
{
$this->dateCreationArticle = $dateCreationArticle;
return $this;
}
public function getImage()
{
return $this->image;
}
public function setImage($image)
{
$this->image = $image;
return $this;
}
public function getAnalyse(): ?array
{
return $this->analyse;
}
public function setAnalyse(array $analyse): self
{
$this->analyse = $analyse;
return $this;
}
}
The form :
<?php
namespace App\Form;
use App\Entity\Algorithme;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AlgorithmeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('titre')
->add('dateCreationArticle',DateType::class,[
'widget' => 'single_text',
// prevents rendering it as type="date", to avoid HTML5 date pickers
'html5' => false,
// adds a class that can be selected in JavaScript
'attr' => ['class' => 'js-datepicker'],
])
->add('image',FileType::class,[
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Algorithme::class,
]);
}
}
And the controller:
<?php
namespace App\Controller;
use App\Entity\Algorithme;
use App\Form\AlgorithmeType;
use App\Repository\AlgorithmeRepository;
use App\Services\CreateFormForArray;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/algorithme")
*/
class AlgorithmeController extends AbstractController
{
/**
* #Route("/new", name="algorithme_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$algorithme = new Algorithme();
$form = $this->createForm(AlgorithmeType::class, $algorithme);
$form->handleRequest($request);
// die();
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$CreateFormForArray = new CreateFormForArray();
$CreateFormForArray->handleArrayString($request,$algorithme,"analyse");
$entityManager->persist($algorithme);
$entityManager->flush();
return $this->redirectToRoute('algorithme_index');
} else {
}
return $this->render('algorithme/new.html.twig', [
'algorithme' => $algorithme,
'form' => $form->createView(),
]);
}
}
I found the problem for those who will wondering is was
#auto_mapping:
# App\Entity\: []
that was uncomment in config/packages/validator.yaml
I'm developing Form Request for Symfony, but i have one problem.
I inject FormRequest instances into controller action using argument value resolving. But I would like to inject services to FormRequest instance without making it public in services.yaml file.
Here is my Resolver:
<?php
namespace App\Resolver;
use App\Exception\FormValidationException;
use App\Request\FormRequest;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ControllerRequestResolver implements ArgumentValueResolverInterface
{
/**
* #var SerializerInterface
*/
private $serializer;
/**
* #var ValidatorInterface
*/
private $validator;
/**
* #var DecoderInterface
*/
private $decoder;
/**
* #var ContainerInterface
*/
private $container;
public function __construct(
SerializerInterface $serializer,
ValidatorInterface $validator,
DecoderInterface $decoder,
ContainerInterface $container
) {
$this->serializer = $serializer;
$this->validator = $validator;
$this->decoder = $decoder;
$this->container = $container;
}
/**
* {#inheritdoc}
* #throws FormValidationException
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
$data = $this->decoder->decode($request->getContent(), 'json');
$request->request->replace($data);
$formRequestClass = $argument->getType();
/** #var FormRequest $form */
$form = $this->container->has($formRequestClass)
? $this->container->get($formRequestClass)
: new $formRequestClass();
$form->initialize(
$request->query->all(), $request->request->all(), $request->attributes->all(),
$request->cookies->all(), $request->files->all(), $request->server->all(), $request->getContent()
);
if (!$form->authorize()) {
throw new AccessDeniedHttpException('Access denied.');
}
$violations = $this->validator->validate($data, $form->rules());
if ($violations->count() > 0) {
throw new FormValidationException($violations, 'Validation error.');
}
yield $form;
}
/**
* {#inheritdoc}
*/
public function supports(Request $request, ArgumentMetadata $argument)
{
return (new \ReflectionClass($argument->getType()))->isSubclassOf(FormRequest::class);
}
}
So to receive FormRequest with needed dependencies in $this->container->get($formRequestClass) I need to make it public.
Here is FormRequest:
<?php
namespace App\Request;
use App\Entity\User;
use App\Rule\UniqueConfig;
use App\Service\Randomizer;
use App\Validator\Constraints\UniqueEntity;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Validator\Constraints as Assert;
class UserRequest extends FormRequest
{
/**
* #var null|Randomizer
*/
private $randomizer;
public function __construct(Randomizer $randomizer)
{
$this->randomizer = $randomizer;
}
public function authorize(): bool
{
return $this->randomizer->getNumber() > 0.5;
}
public function rules(): Assert\Collection
{
return new Assert\Collection([
'email' => [
new Assert\NotBlank(),
new Assert\Email(),
new UniqueEntity([
'config' => new UniqueConfig(User::class, 'u', 'email', function ($value, QueryBuilder $qb) {
// You can add here additional filtering
}),
]),
],
'firstName' => [
new Assert\Length(['max' => 255]),
],
'lastName' => [
new Assert\Length(['max' => 255]),
],
]);
}
}
Thanks to #Cerad for help! Here is total result:
<?php
namespace App\Locator;
use Symfony\Component\DependencyInjection\ServiceLocator;
class FormRequestServiceLocator extends ServiceLocator
{
}
I inject it (FormRequestServiceLocator) into ControllerRequestResolver
<?php
namespace App\DependencyInjection\Compiler;
use App\Locator\FormRequestServiceLocator;
use App\Request\FormRequest;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class FormRequestPass implements CompilerPassInterface
{
/**
* {#inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has(FormRequest::class)) {
return;
}
$taggedServices = $container->findTaggedServiceIds('app.form_request.tag');
$serviceReferences = [];
foreach ($taggedServices as $id => $tags) {
if ($id === FormRequest::class) {
continue;
}
$serviceReferences[$id] = new Reference($id);
}
$driverLocator = $container->getDefinition(FormRequestServiceLocator::class);
$driverLocator->setArguments([$serviceReferences]);
}
}
<?php
namespace App;
// ...
class Kernel extends BaseKernel
{
// ...
protected function build(ContainerBuilder $container)
{
parent::build($container);
$container->registerForAutoconfiguration(FormRequest::class)
->addTag('app.form_request.tag');
$container->addCompilerPass(new FormRequestPass());
}
}
How to create and upload the document using entity where fileType field is embedded in parent form via collectionType. I did read the documentation Symfony Upload. But didn't manage to accomplish this. Always get this error "Type error: Argument 1 passed to App\Service\FileUploader::upload() must be an instance of Symfony\Component\HttpFoundation\File\UploadedFile, instance of App\Entity\Attachment given".
Below is my Invoice entity
class Invoice
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Attachment", mappedBy="invoiceId", cascade={"persist"})
*/
private $attachments;
public function __construct()
{
$this->attachments = new ArrayCollection();
}
/**
* #return Collection|Attachment[]
*/
public function getAttachments(): Collection
{
return $this->attachments;
}
public function addAttachment(Attachment $attachment): self
{
if (!$this->attachments->contains($attachment)) {
$this->attachments[] = $attachment;
$attachment->setInvoiceId($this);
}
return $this;
}
Attachment entity
class Attachment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $path;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Invoice", inversedBy="attachments")
*/
private $invoiceId;
public function getId()
{
return $this->id;
}
public function getPath(): ?string
{
return $this->path;
}
public function setPath(string $path): self
{
$this->path = $path;
return $this;
}
public function getInvoiceId(): ?Invoice
{
return $this->invoiceId;
}
public function setInvoiceId(?Invoice $invoiceId): self
{
$this->invoiceId = $invoiceId;
return $this;
}
Attachment form type
namespace App\Form;
use App\Entity\Attachment;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class AttachmentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('path',FileType::class, array(
'label' => false,
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Attachment::class,
]);
}
}
Invoice form type
namespace App\Form;
use App\Entity\Invoice;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class InvoiceType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('attachments', CollectionType::class, array(
'entry_type' => AttachmentType::class,
'entry_options' => array('label' => false),
'allow_add' => true
))
->add('submit', SubmitType::class, array(
'label' => $options['set_button_label']
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Invoice::class,
'set_button_label' => "Create Invoice",
]);
}
}
and the Controller
namespace App\Controller;
use App\Entity\Invoice;
use App\Form\InvoiceType;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserInterface;
use App\Service\FileUploader;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class InvoiceController extends Controller
{
/**
* #Route("/invoice/create", name="createInvoice")
* #param Request $request
* #param UserInterface $user
* #param FileUploader $fileUploader
* #return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function createInvoice( Request $request, UserInterface $user, FileUploader $fileUploader)
{
Debug::enable();
$invoice = new Invoice();
$form = $this->createForm(InvoiceType::class,$invoice);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
// Prepare upload file
/** #var UploadedFile $files */
$files = $invoice->getAttachments();
foreach($files as $file){
$fileName = $fileUploader->upload($file);
}
$file->move(
$this->getParameter('attachment_directory'),
$fileName
);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($invoice);
$entityManager->flush();
return $this->redirectToRoute('user');
}
return $this->render('invoice/createInvoice.html.twig', [
'controller_name' => 'UserController',
'form' => $form->createView()
]);
}
I think the problem is the FileType field return attachment entity instance while it should return File instance. the question is how do i get the value as File instance?
In your case the property $path type of UploadedFile and not $invoice->getAttachments().
Try to add a property to your Attachement class called $file without doctrine mapping, generate it's getter and setter methods.
/**
* #var UploadedFile
*/
protected $file;
In your AttachmentType class change 'path' => 'file'.
Now, try to update this part in your controller:
$attachements = $invoice->getAttachments();
foreach($attachements as $attachement){
/** #var UploadedFile $file */
$file = $attachement->getFile(); // This is the file
$attachement->setPath($this->fileUploader->upload($file));
}
Please, make your fileUploader service the unique responsible for uploading file, no need to use $file->move().
Error
Catchable Fatal Error: Argument 2 passed to Acme\StoreBundle\Security\TokenAuthenticator::__construct() must be an instance of Doctrine\Common\EventManager, instance of Doctrine\Bundle\MongoDBBundle\ManagerRegistry given, called in D:\xamppNew\htdocs\mtl_project\var\cache\dev\appDevDebugProjectContainer.php on line 6178 and defined
TokenAuthenticator.php
<?php
namespace Acme\StoreBundle\Security;
use Doctrine\Common\EventManager;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\MongoDB\Connection;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
use Doctrine\ODM\MongoDB\Mapping\MappingException;
use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory;
use Doctrine\ODM\MongoDB\Proxy\ProxyFactory;
use Doctrine\ODM\MongoDB\Query\FilterCollection;
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
//use Acme\StoreBundle\Security\TokenAuthenticator;
use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class TokenAuthenticator extends AbstractGuardAuthenticator
{
/**
* #var JWTEncoderInterface
*/
private $jwtEncoder;
/**
* #var Doctrine\Common\Persistence\ObjectManager
*/
protected $om;
/**
* #param JWTEncoderInterface $jwtEncoder
* #param ObjectManager $om
*/
public function __construct(JWTEncoderInterface $jwtEncoder, Doctrine\Common\Persistence\ObjectManager $om)
{
$this->jwtEncoder = $jwtEncoder;
$this->om = $om;
}
/**
* #inheritdoc
*/
public function getCredentials(Request $request)
{
$extractor = new AuthorizationHeaderTokenExtractor(
'Bearer',
'Authorization'
);
$token = $extractor->extract($request);
if (!$token) {
return;
}
return $token;
}
/**
* #inheritdoc
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$data = $this->jwtEncoder->decode($credentials);
if ($data === false) {
throw new CustomUserMessageAuthenticationException('Invalid Token');
}
$username = $data['username'];
return $this->om
->getRepository('UserBundle:User')
->findOneBy(['username' => $username]);
}
/**
* #inheritdoc
*/
public function checkCredentials($credentials, UserInterface $user)
{
return true;
}
/**
* #inheritdoc
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
}
/**
* #inheritdoc
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
}
/**
* #inheritdoc
*/
public function supportsRememberMe()
{
return false;
}
/**
* #inheritdoc
*/
public function start(Request $request, AuthenticationException $authException = null)
{
return new Response('Token is missing!', Response::HTTP_UNAUTHORIZED);
}
}
Refrence
Difference between ObjectManager and EntityManager in Symfony2?
https://github.com/doctrine/mongodb-odm/blob/785c5039d51923d22902fa1249d1e3dd64018838/lib/Doctrine/ODM/MongoDB/DocumentManager.php#L44
im new in symfonymongodb bundle
can anyone suggest how can i use object manager in contructor as symfony is throwing errors.
doctrine_mongodb is a service that returns a Doctrine\Bundle\MongoDBBundle\ManagerRegistry object. You can get ObjectManager by calling getManager from it.
<?php
namespace Acme\StoreBundle\Security;
use Doctrine\Bundle\MongoDBBundle\ManagerRegistry;
// ...
class TokenAuthenticator extends AbstractGuardAuthenticator
{
/**
* #var Doctrine\Common\Persistence\ObjectManager
*/
protected $om;
// ...
public function __construct(JWTEncoderInterface $jwtEncoder, ManagerRegistry $registry)
{
// ...
$this->om = $registry->getManager();
}