Here is my problem:
In fact when I retrieve the logged in user data with $currentUser = $this->getUser();
Afterwards I cannot access the method->getNewPassword(). I am told that it is not defined yet I have previously defined it on the User entity. Could this be due to my version of symfony ?
I don't understand why this error occurs when everything seems very normal.
Here's my User entity file:
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields:"email", message:"Email déjà pris")]
class User implements UserInterface, PasswordAuthenticatedUserInterface {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;// par défault
#[ORM\Column(type: 'string', length: 180, unique: true)]
#[Assert\Email(message:'Veuillez entrer un email valide.')]
#[Assert\NotBlank(message:'Veuillez renseigner votre email.')]
private $email; // par défault
#[ORM\Column(type: 'json')]
private $roles = []; // par défault
#[ORM\Column(type: 'string')]
#[Assert\Length(min: 6, minMessage:'Le mot de passe doit avoir au moins 6 caractères.')]
#[Assert\NotBlank(message:'Veuillez renseigner un mot de passe.')]
private $password; // par défault
#[Assert\Length(min: 6, minMessage:'Le mot de passe doit avoir au moins 6 caractères.')]
private $newPassword;
#[ORM\Column(type: 'string', length: 255)]
#[Assert\NotBlank(message:'Veuillez renseigner votre prénom.')]
private $firstname;
#[ORM\Column(type: 'string', length: 255)]
#[Assert\NotBlank(message:'Veuillez renseigner votre nom.')]
private $lastname;
#[ORM\OneToMany(mappedBy: 'author', targetEntity: Question::class, orphanRemoval: true)]
private $questions;
#[ORM\OneToMany(mappedBy: 'author', targetEntity: Comment::class, orphanRemoval: true)]
private $comments;
#[ORM\Column(type: 'string', length: 255)]
#[Assert\Url(message:'Veuillez renseigner une Url.')]
#[Assert\NotBlank(message:'Veuillez renseigner une image de profil.')]
private $picture;
#[ORM\OneToMany(mappedBy: 'author', targetEntity: Vote::class, orphanRemoval: true)]
private $votes;
public function __construct()
{
$this->Questions = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->votes = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
public function getRoles():array
{
return $this->roles;
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getNewPassword(): ?string
{
return $this->newPassword;
}
public function setNewPassword(string $password): self
{
$this->newPassword = $password;
return $this;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getFirstname(): ?string
{
return $this->firstname;
}
public function setFirstname(string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
public function getLastname(): ?string
{
return $this->lastname;
}
public function setLastname(string $lastname): self
{
$this->lastname = $lastname;
return $this;
}
/**
* #return Collection|Question[]
*/
public function getQuestions(): Collection
{
return $this->questions;
}
public function addQuestion(Question $question): self
{
if (!$this->questions->contains($question)) {
$this->questions[] = $question;
$question->setAuthor($this);
}
return $this;
}
public function removeQuestion(Question $question): self
{
if ($this->Questions->removeElement($question)) {
// set the owning side to null (unless already changed)
if ($question->getAuthor() === $this) {
$question->setAuthor(null);
}
}
return $this;
}
/**
* #return Collection|Comment[]
*/
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setAuthor($this);
}
return $this;
}
public function removeComment(Comment $comment): self
{
if ($this->comments->removeElement($comment)) {
// set the owning side to null (unless already changed)
if ($comment->getAuthor() === $this) {
$comment->setAuthor(null);
}
}
return $this;
}
public function getPicture(): ?string
{
return $this->picture;
}
public function setPicture(string $picture): self
{
$this->picture = $picture;
return $this;
}
public function getFullname(): ?string {
return $this->firstname.' '.$this->lastname;
}
/**
* #return Collection|Vote[]
*/
public function getVotes(): Collection
{
return $this->votes;
}
public function addVote(Vote $vote): self
{
if (!$this->votes->contains($vote)) {
$this->votes[] = $vote;
$vote->setAuthor($this);
}
return $this;
}
public function removeVote(Vote $vote): self
{
if ($this->votes->removeElement($vote)) {
// set the owning side to null (unless already changed)
if ($vote->getAuthor() === $this) {
$vote->setAuthor(null);
}
}
return $this;
}
}
Here's my UserController.php file:
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\ManagerRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class UserController extends AbstractController
{
#[Route('/user/{id}', name: 'user')]
#[IsGranted('IS_AUTHENTICATED_REMEMBERED')]
public function userProfile(User $user): Response {
$currentUser = $this->getUser();
if($currentUser === $user) {
return $this->redirectToRoute('current_user');
}
return $this->render('user/show.html.twig', [
'user' => $user,
]);
}
#[Route('/myprofile', name: 'current_user')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function currentUserProfile(Request $request, EntityManagerInterface $em, UserPasswordHasherInterface $passwordHasher): Response
{
$currentUser = $this->getUser(); // on récupère l'utilisateur connecté.
$userForm = $this->createForm(UserType::class, $currentUser); // création du formulaire
$userForm->remove('password'); // suppression du champ password
$userForm->add('newPassword', PasswordType::class, ['label' => 'Nouveau mot de passe']);
$userForm->handleRequest($request);
if($userForm->isSubmitted() && $userForm->isValid()) {
// dump($currentUser);
$newPassword = $currentUser->getNewPassword();
if($newPassword) {
$hash = $passwordHasher->hashPassword($currentUser, $newPassword);
$currentUser->setPassword($hash);
}
$em->flush();
// dump($currentUser);
}
return $this->render('user/index.html.twig', [
'form' => $userForm->createView(),
]);
}
}
Here is my UserType form:
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', null, ['label' =>'*Email'])
->add('firstname', null, ['label' =>'*Prénom'])
->add('lastname', null, ['label' =>'*Nom'])
->add('picture', null, ['label' =>'*Image'])
->add('password', PasswordType::class, ['label' =>'*Mot de passe','empty_data' => ''])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
I think I was clear about my question. If you need additional information to help me please leave it in the comments. Please I am waiting for answers to my question.
Related
Hello to the community,
I would like your help with two problems I am having with sending documents via my form and how I can display them in easyadmin please.
I've been stuck for some time on these two problems and all the answers I've found on the net haven't managed to get me out of this situation :'(
My documentsTypephp
<?php
namespace App\Form;
use App\Entity\Documents;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\File;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class DocumentsType extends AbstractType
{
public function buildForm(
FormBuilderInterface $builder,
array $options
): void {
$builder
->add('description', TextType::class, [
'label' => 'Description',
'attr' => [
'placeholder' => 'Description',
],
])
->add('ui_ux', FileType::class, [
'multiple' => false,
'data_class' => null,
'label' => ' Si UX/ UI',
'mapped' => true,
'required' => false,
'empty_data' => '',
'constraints' => [
new File([
'maxSize' => '1024k',
'mimeTypes' => [
'application/pdf',
'application/x-pdf',
'application/doc',
'application/docx',
'application/figx',
'application/odt',
'application/ott',
'application/jpg',
],
'mimeTypesMessage' =>
'Please upload a valid PDF, document, Figma file',
]),
],
])
this is my documentsphp
<?php
namespace App\Entity;
use App\Repository\DocumentsRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: DocumentsRepository::class)]
class Documents
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column]
private ?\DateTime $sending_date = null;
#[ORM\ManyToOne(inversedBy: 'documents')]
#[ORM\JoinColumn(nullable: true)]
private ?User $users = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $description = null;
#[ORM\Column(length: 400, nullable: true)]
private ?string $ui_ux = null;
#[ORM\Column(length: 400, nullable: true)]
private ?string $seo = null;
#[ORM\Column(length: 400, nullable: true)]
private ?string $image_service = null;
#[ORM\ManyToMany(targetEntity: Services::class, inversedBy: 'documents')]
private Collection $services;
public function __construct()
{
$this->services = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getSendingDate(): ?\DateTime
{
return $this->sending_date;
}
public function setSendingDate(\DateTime $sending_date): self
{
$this->sending_date = $sending_date;
return $this;
}
public function getUsers(): ?User
{
return $this->users;
}
public function setUsers(?User $users): self
{
$this->users = $users;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getUiUx(): ?string
{
return $this->ui_ux;
}
public function setUiUx(?string $ui_ux): self
{
$this->ui_ux = $ui_ux;
return $this;
}
public function getSeo(): ?string
{
return $this->seo;
}
public function setSeo(?string $seo): self
{
$this->seo = $seo;
return $this;
}
public function getImageService(): ?string
{
return $this->image_service;
}
public function setImageService(?string $image_service): self
{
$this->image_service = $image_service;
return $this;
}
/**
* #return Collection<int, Services>
*/
public function getServices(): Collection
{
return $this->services;
}
public function addService(Services $service): self
{
if (!$this->services->contains($service)) {
$this->services->add($service);
}
return $this;
}
public function removeService(Services $service): self
{
$this->services->removeElement($service);
return $this;
}
}
and my second probleme realted to this one is how can i watch the documents in my easyadmin ?
I have this when i can send just one document
thank you very much indeed for your time to you all.
I've got a problem.
When I want to create an article, it shows me this error.
And when I want to edit one, it doesn't change anything.
It was working before, but then I've added category and comment section.
Can you help me ?
here is my Controller:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\Article;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension;
use Symfony\Component\Form\Forms;
use Symfony\Component\HttpFoundation\Request;
use App\Form\ArticleType;
class BlogController extends AbstractController
{
#[Route('/blog', name: 'blog')]
public function index(ManagerRegistry $doctrine): Response
{
$articles = $doctrine->getRepository(Article::class)->findAll();
return $this->render('blog/index.html.twig', [
'controller_name' => 'BlogController',
'articles' => $articles,
]);
}
#[Route("/", name: 'home')]
public function home() {
return $this->render('blog/home.html.twig', [
'title' => "Bienvenue",
'age' => 31,
]);
}
#[Route("/blog/new", name: 'blog_create')]
#[Route("/blog/{id}/edit", name: 'blog_edit')]
public function form(Article $article = null, Request $request, EntityManagerInterface $manager) {
// si on a pas d'article, donc on est en new
if(!$article){
$article = new Article();
}
$form = $this->createForm(ArticleType::class);
$form->handleRequest($request);
//si on a appuyer sur le bouton et que infos valides, on peut envoyer
if($form->isSubmitted() && $form->isValid()){
$manager->persist($article);
$manager->flush();
// on redirige vers l'article crée ou modifié
return $this->redirectToRoute('blog_show', ['id' => $article->getId()]);
}
return $this->render('blog/create.html.twig', [
'formArticle' => $form->createView(),
// edit Mode a true si il y a un article
'editMode' => $article->getId() !== null,
]);
}
#[Route("/blog/{id}", name: 'blog_show')]
public function show(ManagerRegistry $doctrine , $id){
$repo = $doctrine->getRepository(Article::class);
$article = $repo->find($id);
return $this->render('blog/show.html.twig', [
'article' => $article,
]);
}
}
And now, my form:
<?php
namespace App\Form;
use App\Entity\Article;
use App\Entity\Category;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title')
->add('category', EntityType::class, [
'class' => Category::class,
'choice_label' => 'title',
])
->add('content')
->add('image')
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Article::class,
]);
}
}
To end with my Entities:
Article
<?php
namespace App\Entity;
use App\Repository\ArticleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
#[Assert\Length(
min: 2,
max: 50,
)]
private $title;
#[ORM\Column(type: 'text')]
private $content;
#[ORM\Column(type: 'string', length: 255)]
private $image;
#[ORM\ManyToOne(targetEntity: Category::class, inversedBy: 'articles')]
private $category;
#[ORM\OneToMany(mappedBy: 'article', targetEntity: Comment::class)]
private $comments;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
/**
* #return Collection<int, Comment>
*/
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setArticle($this);
}
return $this;
}
public function removeComment(Comment $comment): self
{
if ($this->comments->removeElement($comment)) {
// set the owning side to null (unless already changed)
if ($comment->getArticle() === $this) {
$comment->setArticle(null);
}
}
return $this;
}
}
Category
<?php
namespace App\Entity;
use App\Repository\CategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: CategoryRepository::class)]
class Category
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $title;
#[ORM\Column(type: 'text', nullable: true)]
private $description;
#[ORM\OneToMany(mappedBy: 'category', targetEntity: Article::class)]
private $articles;
public function __construct()
{
$this->articles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
/**
* #return Collection<int, Article>
*/
public function getArticles(): Collection
{
return $this->articles;
}
public function addArticle(Article $article): self
{
if (!$this->articles->contains($article)) {
$this->articles[] = $article;
$article->setCategory($this);
}
return $this;
}
public function removeArticle(Article $article): self
{
if ($this->articles->removeElement($article)) {
// set the owning side to null (unless already changed)
if ($article->getCategory() === $this) {
$article->setCategory(null);
}
}
return $this;
}
}
Thanks in advance guys :))
I won't add the twig file cause it's normal file
I succeeded.
In the controllers, I just added:
$this->createForm(ArticleType::class, $article);
I'm trying to create/expand upon a basic app following the Symfony Sonata Admin Bundle documentation. There are BlogPost, Category, and Author entities and their respective admins. BlogPost has a ManyToOne relationship to both Category and Author.
I notice from the start that I had trouble applying different Form Types to 'Category' but thought maybe I was missing something. After adding 'Author,' running migrations, etc I found that I was able to add form types other than ModelType, such as ModelAutocompleteType or ModelListType to my author listing on BlogPostAdmin's configureFormFields without any issue. However when I tried to do the same to Category I'd get the error:
The current field `category` is not linked to an admin. Please create one for the target model: ``.
I have dropped and recreated the database and caches, even created a new MyCategory entity in case something was wrong when Category was generated. (Provided in examples)
I cannot seem to figure out why I can add different form types to Author but not Category.
Entities:
Entity/BlogPost.php
<?php
namespace App\Entity;
use App\Repository\BlogPostRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: BlogPostRepository::class)]
class BlogPost
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $title;
#[ORM\Column(type: 'boolean', nullable: true)]
private $draft;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $body;
#[ORM\ManyToOne(targetEntity: Author::class, inversedBy: 'posts')]
private $author;
#[ORM\ManyToOne(targetEntity: MyCategory::class, inversedBy: 'posts')]
private $myCategory;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function isDraft(): ?bool
{
return $this->draft;
}
public function setDraft(bool $draft): self
{
$this->draft = $draft;
return $this;
}
public function getBody(): ?string
{
return $this->body;
}
public function setBody(?string $body): self
{
$this->body = $body;
return $this;
}
public function getAuthor(): ?Author
{
return $this->author;
}
public function setAuthor(?Author $author): self
{
$this->author = $author;
return $this;
}
public function getMyCategory(): ?MyCategory
{
return $this->myCategory;
}
public function setMyCategory(?MyCategory $myCategory): self
{
$this->myCategory = $myCategory;
return $this;
}
}
Entity/Author.php
<?php
namespace App\Entity;
use App\Repository\AuthorRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: AuthorRepository::class)]
class Author
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $name;
#[ORM\OneToMany(mappedBy: 'author', targetEntity: BlogPost::class)]
private $posts;
public function __construct()
{
$this->posts = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection<int, BlogPost>
*/
public function getPosts(): Collection
{
return $this->posts;
}
public function addPost(BlogPost $post): self
{
if (!$this->posts->contains($post)) {
$this->posts[] = $post;
$post->setAuthor($this);
}
return $this;
}
public function removePost(BlogPost $post): self
{
if ($this->posts->removeElement($post)) {
// set the owning side to null (unless already changed)
if ($post->getAuthor() === $this) {
$post->setAuthor(null);
}
}
return $this;
}
public function __toString(): string
{
return $this->getName();
}
}
Entity/MyCategory.php
<?php
namespace App\Entity;
use App\Repository\MyCategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: MyCategoryRepository::class)]
class MyCategory
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $name;
#[ORM\OneToMany(mappedBy: 'myCategory', targetEntity: BlogPost::class)]
private $posts;
public function __construct()
{
$this->posts = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection<int, BlogPost>
*/
public function getPosts(): Collection
{
return $this->posts;
}
public function addPost(BlogPost $post): self
{
if (!$this->posts->contains($post)) {
$this->posts[] = $post;
$post->setMyCategory($this);
}
return $this;
}
public function removePost(BlogPost $post): self
{
if ($this->posts->removeElement($post)) {
// set the owning side to null (unless already changed)
if ($post->getMyCategory() === $this) {
$post->setMyCategory(null);
}
}
return $this;
}
}
Admins:
Admin/BlogPostAdmin.php
<?php
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\DataGrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use App\Entity\MyCategory;
use App\Entity\Author;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\Type\ModelType;
use Sonata\AdminBundle\Form\Type\ModelListType;
use App\Entity\BlogPost;
use Sonata\AdminBundle\Form\Type\ModelAutocompleteType;
final class BlogPostAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form
->tab('Post')
->with("Content", ['class' => 'col-md-9'])
->add('title', TextType::class)
->add('body', TextareaType::class)
->end()
->with('Meta data', ['class' => 'col-md-3'])
->add('mycategory', ModelAutocompleteType::class, ['property' => 'name']) // Causing the error
->add('author', ModelAutocompleteType::class, ['property' => 'name'])
->add('draft')
->end()
->end()
->tab('Publish Options')
// ...
->end();
}
protected function configureListFields(ListMapper $list): void
{
$list
->addIdentifier('title')
->add('author.name')
->add('draft');
}
protected function configureDatagridFilters(DatagridMapper $datagrid): void
{
$datagrid
->add('title')
->add('author', null, [
'field_type' => EntityType::class,
'field_options' => [
'class' => Author::class,
'choice_label' => 'name',
],
])
;
}
protected function configureShowFields(ShowMapper $show): void
{
$show
->tab('Post')
->with('Content', ['class' => 'col-md-9'])
->add('id')
->add('title')
->add('body')
->add('author.name')
->end()
->with('Meta data', ['class' => 'col-md-3'])
// ->add('category.name')
->end()
->end()
}
public function toString(object $object): string
{
return $object instanceof BlogPost ? $object->getTitle() : 'Blog Post'; // Shown in the breadcrumb on the create view
}
}
Admin/AuthorAdmin.php
<?php
namespace App\Admin;
use App\Entity\Author;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
final class AuthorAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form->add('name', TextType::class);
}
protected function configureDatagridFilters(DatagridMapper $datagrid): void
{
$datagrid->add('name');
}
protected function configureListFields(ListMapper $list): void
{
$list->addIdentifier('name');
}
protected function configureShowFields(ShowMapper $show): void
{
$show->add('name');
}
public function toString(object $object): string
{
return $object instanceof Author ? $object->getName() : 'Author'; // Shown in the breadcrumb on the create view
}
}
Admin/CategoryAdmin.php
<?php
namespace App\Admin;
use App\Entity\MyCategory;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
final class MyCategoryAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form->add('name', TextType::class);
}
protected function configureDatagridFilters(DatagridMapper $datagrid): void
{
$datagrid->add('name');
}
protected function configureListFields(ListMapper $list): void
{
$list->addIdentifier('name');
}
protected function configureShowFields(ShowMapper $show): void
{
$show->add('name');
}
public function toString(object $object): string
{
return $object instanceof MyCategory ? $object->getName() : 'Category'; // Shown in the breadcrumb on the create view
}
}
Config:
config/services.yaml
parameters:
locale: "en"
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
admin.category:
class: App\Admin\MyCategoryAdmin
tags:
- {
name: sonata.admin,
model_class: App\Entity\MyCategory,
manager_type: orm,
label: Category,
}
admin.blog_post:
class: App\Admin\BlogPostAdmin
tags:
- {
name: sonata.admin,
model_class: App\Entity\BlogPost,
manager_type: orm,
label: "Blog post",
}
admin.author:
class: App\Admin\AuthorAdmin
tags:
- {
name: sonata.admin,
model_class: App\Entity\Author,
manager_type: orm,
label: "Author",
}
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: "../src/"
exclude:
- "../src/DependencyInjection/"
- "../src/Entity/"
- "../src/Kernel.php"
I'm trying to learn Symfony on my own and I have a lot of questions
I'm making a news portal. The administrator can download news from an Excel file. I am converting a file to an associative array. For example:
[ 'Title' => 'Some title',
'Text' => 'Some text',
'User' => 'example#example.com',
'Image' => 'https://loremflickr.com/640/360'
]
I then create a form to validate the fields in the file. There are no problems with the fields "Title", "Text", "Image". For "User" I created a Custom Validation Constraint because the User specifies an email in the file and I want to check if a user with that email exists in the database.
Validation file
class NewsImportType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, [
'constraints' =>
[
new NotBlank(),
new Length(['min' => 256])
],
])
->add('text', TextareaType::class, [
'constraints' =>
[
new NotBlank(),
new Length(['max' => 1000])
],
])
->add('user', TextType::class, [
'constraints' =>
[
new NotBlank(),
new Email(),
new UserExists(),
],
])
->add('image', TextType::class, [
'constraints' =>
[
new NotBlank(),
new Length(['max' => 256]),
new Url()
],
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'allow_extra_fields' => true,
'data_class' => News::class,
]);
}
}
Custom Validation Constraint for User
class UserExistsValidator extends ConstraintValidator
{
public function __construct(private UserRepository $repository)
{
}
public function validate($email, Constraint $constraint)
{
if (!$constraint instanceof UserExists) {
throw new UnexpectedTypeException($constraint, UserAccountExists::class);
}
if (null === $email || '' === $email) {
return;
}
if (!is_string($email)) {
throw new UnexpectedValueException($email, 'string');
}
if (!$this->userExists($email)) {
$this->context->buildViolation($constraint->message)->addViolation();
}
}
private function userExists(string $email): bool
{
$user = $this->repository->findOneBy(array('email' => $email));
return null !== $user;
}
}
But the problem is that I am getting the error:
Expected argument of type "?App\Entity\User", "string" given at
property path "user".
As I understand it, the reason is that I bind the form to Entity News and the User field must be an object, not a string ('email').
App\Entity\News.php
class News implements EntityInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $title;
#[ORM\Column(type: 'text')]
private $text;
#[ORM\Column(type: 'string', length: 1000)]
private $image;
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'news')]
#[ORM\JoinColumn(nullable: true)]
private $user;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(string $text): self
{
$this->text = $text;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
Please tell me how can I fix this problem.
UPDATE
Trying to create a DataTransformer
EmailToUserTransformer.php
class EmailToUserTransformer implements DataTransformerInterface
{
public function __construct(private UserRepository $repository)
{
}
//Трансформируем email в User
public function transform($email): ?User
{
if (!$email) {
return null;
}
$user = $this->repository->findOneBy(array('email' => $email));
if (null === $user) {
throw new TransformationFailedException(sprintf(
'A user with email "%s" does not exist!',
$email
));
}
return $user;
}
//Трансформируем User в email
public function reverseTransform($user): string
{
if (!$user) {
return '';
}
return $user->getEmail();
}
}
I get the error
"Too few arguments to function App\Form\NewsImportType::__construct(),
0 passed in .. and exactly 1 expected"
Could this have something to do with how I'm building the form?
protected function buildForm(string $formType, array $formOptions = []): FormInterface
{
$validator = $this->validator;
$formFactory = Forms::createFormFactoryBuilder()
->addExtension(new ValidatorExtension($validator))
->getFormFactory();
$baseOptions = [
'allow_extra_fields' => true
];
return $formFactory
->create(
$formType,
null,
array_merge($formOptions, $baseOptions)
);
}
You could use a DataTransformer
// src/Form/DataTransformer/EmailToUserTransformer.php
namespace App\Form\DataTransformer;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class EmailToUserTransformer implements DataTransformerInterface
{
public function __construct(private EntityManagerInterface $entityManager)
{
}
public function transform(string $email): ?User
{
if (null === $email) {
return null;
}
$user = $this->entityManager
->getRepository(User::class)
->findOneBy(['email' => $email);
if (null === $user) {
throw new TransformationFailedException(sprintf(
'A user with email "%s" does not exist!',
$email
));
}
return $user;
}
public function reverseTransform(User $user): string
{
if (!$user) {
return '';
}
return $user->getEmail();
}
}
Then add the transformer in your form builder:
public function __construct(private EmailToUserTransformer $transformer)
{
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// Rest of form here
$builder->get('user')
->addModelTransformer($this->transformer);
}
I use symfony6 for the first time with php 8.0.17. I want to make a small form that allows me to retrieve "Card" objects. My "Card" objects are bound to many to many color properties. I thought I could retrieve the cards objects via a findBy but I have the following error: Warning: Trying to access array offset on value of type null.
I looked at the documentation but I don't understand where my error comes from.
Cannot use findBy with related object ? Or maybe it's not a good idea to use entity field in form ? As you see the code is simple and it's that i don't understand it's not okay
The code is here :
My Card entity :
<?php
namespace App\Entity;
use App\Repository\CardRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: CardRepository::class)]
class Card
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\ManyToMany(targetEntity: Color::class, inversedBy: 'card')]
private $color;
public function __construct()
{
$this->color = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* #return Collection<int, Color>
*/
public function getColor(): Collection
{
return $this->color;
}
public function addColor(Color $color): self
{
if (!$this->color->contains($color)) {
$this->color[] = $color;
}
return $this;
}
public function removeColor(Color $color): self
{
$this->color->removeElement($color);
return $this;
}
}
My Color entity :
<?php
namespace App\Entity;
use App\Repository\ColorRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ColorRepository::class)]
class Color
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $name;
#[ORM\ManyToMany(targetEntity: Card::class, mappedBy: 'color')]
private $card;
public function __construct()
{
$this->card = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function __toString()
{
return $this->getName();
}
/**
* #return Collection<int, Card>
*/
public function getCard(): Collection
{
return $this->card;
}
public function addCard(Cards $card): self
{
if (!$this->card->contains($card)) {
$this->card[] = $card;
$card->addColor($this);
}
return $this;
}
public function removeCard(Cards $card): self
{
if ($this->cards->removeElement($card)) {
$card->removeColor($this);
}
return $this;
}
}
My Search Form :
<?php
namespace App\Form;
use App\Entity\Color;
use App\Entity\Keyword;
use App\Entity\Type;
use App\Form\DataTransformer\ColorToStringTransformer;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
class CardSearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['required' => false])
->add('color', EntityType::class, [
'class' => Color::class,
// uses the Cards.name property as the visible option string
'choice_label' => 'name',
// used to render a select box, check boxes or radios
// 'multiple' => true,
// 'expanded' => true,
// To add little test for empty value
'placeholder' => 'Choose a color.',
'required' => false,
])
->add('save', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
}
}
And to finish the controller action :
class DeckBuilderController extends AbstractController
{
#[Route('/deck/builder', name: 'app_deck_builder')]
public function index(Request $request, CardRepository $cardRepository): Response
{
$form = $this->createForm(CardSearchType::class);
$form->handleRequest($request);
$cards = null;
if ($form->isSubmitted() && $form->isValid())
{
// $form->getData() holds the submitted values
$cardData = $form->getData();
$cards = $cardRepository->findBy($cardData); => error Warning: Trying to access array offset on value of type null
}
return $this->render('deck_builder/index.html.twig', [
'controller_name' => 'DeckBuilderController',
'form' => $form->createView(),
'cards' => $cards,
]);
}
Thank in advance for your response.
$form->getData() doesn't return an array but an object with the form content.
My guess is that you want to use $form->get('color')->getData() instead
Correcting your code, I would try something like this :
public function index(Request $request, CardRepository $cardRepository): Response {
$cards = array();
$form = $this->createForm(CardSearchType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$cards = $cardRepository->findBy(array('color'=>$form->get('color')->getData()));
}
return $this->renderForm('deck_builder/index.html.twig', array(
'controller_name' => 'DeckBuilderController',
'form' => $form,
'cards' => $cards,
));
}
To solve my problem I finally created a query by hand and made the join in DQL.