How to solve schema update error with doctrine? - database

I'm having an issue on a pretty simple application made with Symfony 4.3.4
I just updated an entity and I'm trying to update my database with "doctrine:schema:update --force", but I have here an issue : An exception occurred while executing 'ALTER TABLE lead ADD content LONGTEXT DEFAULT NULL'
Second message : Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lead ADD content LONGTEXT DEFAULT NULL' at line 1
I tried to change the name of the "content" attribute (it's the one I added), but this did not work. Do someone know why this is happening ?
Here is my Lead entity :
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\LeadRepository")
*/
class Lead
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\Column(type="boolean")
*/
private $isAuth = false;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Formation", inversedBy="leads")
* #ORM\JoinColumn(nullable=false)
*/
private $formation;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $content;
public function __construct()
{
$this->createdAt = new \DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getIsAuth(): ?bool
{
return $this->isAuth;
}
public function setIsAuth(bool $isAuth): self
{
$this->isAuth = $isAuth;
return $this;
}
public function getFormation(): ?Formation
{
return $this->formation;
}
public function setFormation(?Formation $formation): self
{
$this->formation = $formation;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(?string $content): self
{
$this->content = $content;
return $this;
}
}

"lead" seems to be a reserved keyword for MySQL, so you need to escape it with backticks as follows:
/**
* #ORM\Entity(repositoryClass="App\Repository\LeadRepository")
* #ORM\Table(name="`lead`")
*/
You need to do this for other common keywords used as field names such as "order" and "sort" too.

Related

Error with array collection with a one to many relation

I have a problem with a OneToMany relation and "array collection" between my entity CV and my entities, Formation, Skills and Experiences. A CV can have several formation, experiences or skills.
Even with Symfony documentation, I'm not even sure to get the issues.
Here is my Skills entity (formation and experiences are the same)
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\SkillsRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=SkillsRepository::class)
*/
class Skills
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $type;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\Column(type="string", length=255)
*/
private $level;
/**
* #ORM\ManyToOne(targetEntity=Cv::class, inversedBy="id_skills")
* #ORM\JoinColumn(nullable=false)
*/
private $id_cv;
public function getId(): ?int
{
return $this->id;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getLevel(): ?string
{
return $this->level;
}
public function setLevel(string $level): self
{
$this->level = $level;
return $this;
}
public function getIdCv(): ?Cv
{
return $this->id_cv;
}
public function setIdCv(?Cv $id_cv): self
{
$this->id_cv = $id_cv;
return $this;
}
}
And my CV entity:
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\CvRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=CvRepository::class)
*/
class Cv
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity=Formation::class, mappedBy="id_cv", orphanRemoval=true)
*/
private $id_formation;
/**
* #ORM\OneToMany(targetEntity=Experiences::class, mappedBy="id_cv", orphanRemoval=true)
*/
private $id_experiences;
/**
* #ORM\OneToMany(targetEntity=Skills::class, mappedBy="id_cv", orphanRemoval=true)
*/
private $id_skills;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\Column(type="string", length=255)
*/
private $url_perso;
/**
* #ORM\Column(type="string", length=255)
*/
private $video;
/**
* #ORM\OneToOne(targetEntity=Candidate::class, mappedBy="id_cv", cascade={"persist", "remove"})
*/
private $id_candidate;
public function __construct()
{
$this->id_formation = new ArrayCollection();
$this->id_experiences = new ArrayCollection();
$this->id_skills = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* #return Collection|Formation[]
*/
public function getIdFormation(): Collection
{
return $this->id_formation;
}
public function addIdFormation(Formation $id_cv): self
{
if (!$this->id_formation->contains($id_cv)) {
$this->id_formation[] = $id_cv;
$id_cv->setIdCv($this);
}
return $this;
}
public function removeIdFormation(Formation $id_cv): self
{
if ($this->id_formation->removeElement($id_cv)) {
// set the owning side to null (unless already changed)
if ($id_cv->getIdCv() === $this) {
$id_cv->setIdCv(null);
}
}
return $this;
}
/**
* #return Collection|Experiences[]
*/
public function getIdExperiences(): Collection
{
return $this->id_experiences;
}
public function addIdExperience(Experiences $idExperience): self
{
if (!$this->id_experiences->contains($idExperience)) {
$this->id_experiences[] = $idExperience;
$idExperience->setIdCv($this);
}
return $this;
}
public function removeIdExperience(Experiences $idExperience): self
{
if ($this->id_experiences->removeElement($idExperience)) {
// set the owning side to null (unless already changed)
if ($idExperience->getIdCv() === $this) {
$idExperience->setIdCv(null);
}
}
return $this;
}
/**
* #return Collection|Skills[]
*/
public function getIdSkills(): Collection
{
return $this->id_skills;
}
public function addIdSkill(Skills $idSkill): self
{
if (!$this->id_skills->contains($idSkill)) {
$this->id_skills[] = $idSkill;
$idSkill->setIdCv($this);
}
return $this;
}
public function removeIdSkill(Skills $idSkill): self
{
if ($this->id_skills->removeElement($idSkill)) {
// set the owning side to null (unless already changed)
if ($idSkill->getIdCv() === $this) {
$idSkill->setIdCv(null);
}
}
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getUrlPerso(): ?string
{
return $this->url_perso;
}
public function setUrlPerso(string $url_perso): self
{
$this->url_perso = $url_perso;
return $this;
}
public function getVideo(): ?string
{
return $this->video;
}
public function setVideo(string $video): self
{
$this->video = $video;
return $this;
}
public function getIdCandidate(): ?Candidate
{
return $this->id_candidate;
}
public function setIdCandidate(?Candidate $id_candidate): self
{
// unset the owning side of the relation if necessary
if ($id_candidate === null && $this->id_candidate !== null) {
$this->id_candidate->setIdCv(null);
}
// set the owning side of the relation if necessary
if ($id_candidate !== null && $id_candidate->getIdCv() !== $this) {
$id_candidate->setIdCv($this);
}
$this->id_candidate = $id_candidate;
return $this;
}
}
In my CV entity, array collection and collection are underlined in red.I try to create my REST API.
When I do a post to create a CV, I have this error message:
Could not denormalize object of type
"Doctrine\Common\Collections\Collection", no supporting
normalizer found.
Do you have a clue about what's wrong?

Does Sonata admin work with self generated entity ids?

Does Sonata admin work self generated entity ids? I have the following entity which uses the Uuid library to generate its own id but when I try to create a new Group using Sonata admin it gets confused and thinks I am editing an existing entity and not creating a new one.
<?php declare(strict_types=1);
namespace App\Entity;
use App\Value\StartEndTime;
use App\Exception\GroupInactiveException;
use Ramsey\Uuid\Uuid;
use Assert\Assertion;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
class Group
{
/** #var string */
private $id;
/** #var string */
private $title;
/** #var string */
private $description;
/** #var User */
private $admin;
/** #var Collection */
private $members;
/** #var Collection */
private $invites;
/** #var StartEndTime */
private $startEndTime;
/** #var StartEndTime */
private $eventStartEndTime;
public function __construct()
{
$this->id = Uuid::uuid4()->toString();
$this->members = new ArrayCollection();
$this->invites = new ArrayCollection();
}
public function getId(): string
{
return $this->id;
}
public function getTitle()
{
return $this->title;
}
public function setTitle(string $title)
{
Assertion::notEmpty($title, 'Title is not specified');
$this->title = $title;
return $this;
}
public function getDescription()
{
return $this->description;
}
public function setDescription(string $description)
{
Assertion::notEmpty($description, 'Description is not specified');
$this->description = $description;
return $this;
}
public function getStartEndTime()
{
return $this->startEndTime;
}
public function setStartEndTime(StartEndTime $startEndTime)
{
$this->startEndTime = $startEndTime;
return $this;
}
public function getEventStartEndTime()
{
return $this->eventStartEndTime;
}
public function setEventStartEndTime(StartEndTime $startEndTime)
{
$this->eventStartEndTime = $startEndTime;
return $this;
}
}

Symfony entity set array as single entity elements

i have problem, i would to add prototype array to database but this show me this error:
Expected argument of type "AppBundle\Entity\Tag", "array" given
...
Post ->setTag (array(array('value' => 'test'), array('value' => 'tess')))
here is my setter for tag:
public function setTag(\AppBundle\Entity\Tag $tag = null)
{
$this->tag = $tag;
return $this;
}
I Have two entities with relation, here relation:
class Post
{
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="post")
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
*/
private $tag;
public function setTag(\AppBundle\Entity\Tag $tag = null)
{
$this->tag = $tag;
return $this;
}
}
and tag:
class Tag
{
/**
* #ORM\ManyToMany(targetEntity="Post", mappedBy="tag")
*/
private $post;
}
Source:
http://snipet.co.uk/kR
http://snipet.co.uk/gcf
http://snipet.co.uk/0VI
You're trying to model a bidirectional many-to-many relation between Post and Tag.
So, first of all, your getters need to return a collection of objects, and your setters need to accept a collection of objects - not only one single object as in your code (your setTag method accepts a parameter of type Tag - but you need an array-like parameter).
Secondly, the Doctrine framework does not work with simple PHP arrays, but with implementations of \Doctrine\Common\Collections\Collection.
Next, you need to initialize your collection fields in the constructors of your entity classes with an implementation of the Collection class - you can use \Doctrine\Common\Collections\ArrayCollection.
So your entity classes should look rather like this:
/**
* #ORM\Entity
*/
class Post
{
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="posts")
* #ORM\JoinTable(name="posts_tags")
*/
private $tags;
public function __construct()
{
$this->tags = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getTags()
{
return $this->tags;
}
public function setTags(\Doctrine\Common\Collections\Collection $tags)
{
$this->tags = $tags;
}
}
/**
* #ORM\Entity
*/
class Tag
{
/**
* #ORM\ManyToMany(targetEntity="Post", mappedBy="tags")
*/
private $posts;
public function __construct()
{
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getPosts()
{
return $this->posts;
}
public function setPosts(\Doctrine\Common\Collections\Collection $posts)
{
$this->posts = $posts;
}
}
I strongly advise you to read once again the documentation of the Doctrine framework, how to annotate your entities, and how to model relations: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html

symfony2 file lost upon form error

I am using a standard implementation of file upload in connection with doctrine, as per the example on the symfony2 website tutorials.
When my upload form encounters an error in validation, and sends the user back to the form with error messages, it looses the file chosen for upload, although if I var_dump my $entity->file I can see that it has the file...
//if form is valid, do some stuff... if not:
else {
//var_dump($entity->file); //This works, I get my file
//die;
//Get and check the folder chosen as parent
$entity->setFolder( $this->checkFolderId($request->request->get('folder')) ); //will cause die() if folder doesn't belong to this company
$folders = $this->getFolders();
return $this->render('BizTVMediaManagementBundle:Image:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
'folders' => $folders,
'fileExists' => $fileExists,
));
}
After this is put to the twig view, there is nothing in the file field.
Here is my entity...
<?php
namespace BizTV\MediaManagementBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* BizTV\MediaManagementBundle\Entity\Image
*
* #ORM\Table(name="image")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Image
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255)
* #Assert\NotBlank
*/
private $name;
/**
* #var integer $width
*
* #ORM\Column(name="width", type="integer")
*/
private $width;
/**
* #var integer $height
*
* #ORM\Column(name="height", type="integer")
*/
private $height;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $path;
/**
* #var object BizTV\BackendBundle\Entity\company
*
* #ORM\ManyToOne(targetEntity="BizTV\BackendBundle\Entity\company")
* #ORM\JoinColumn(name="company", referencedColumnName="id", nullable=false)
*/
protected $company;
/**
* #var object BizTV\MediaManagementBundle\Entity\Folder
*
* #ORM\ManyToOne(targetEntity="BizTV\MediaManagementBundle\Entity\Folder")
* #ORM\JoinColumn(name="folder", referencedColumnName="id", nullable=true)
*/
protected $folder;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$this->path = sha1(uniqid(mt_rand(), true)).'.'.$this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getWebPath()
{
return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
return 'uploads/images';
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set width
*
* #param integer $width
*/
public function setWidth($width)
{
$this->width = $width;
}
/**
* Get width
*
* #return integer
*/
public function getWidth()
{
return $this->width;
}
/**
* Set height
*
* #param integer $height
*/
public function setHeight($height)
{
$this->height = $height;
}
/**
* Get height
*
* #return integer
*/
public function getHeight()
{
return $this->height;
}
/**
* Set path
*
* #param string $path
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
/**
* Set company
*
* #param BizTV\BackendBundle\Entity\company $company
*/
public function setCompany(\BizTV\BackendBundle\Entity\company $company)
{
$this->company = $company;
}
/**
* Get company
*
* #return BizTV\BackendBundle\Entity\company
*/
public function getCompany()
{
return $this->company;
}
/**
* Set folder
*
* #param BizTV\MediaManagementBundle\Entity\Folder $folder
*/
public function setFolder(\BizTV\MediaManagementBundle\Entity\Folder $folder = NULL)
{
$this->folder = $folder;
}
/**
* Get folder
*
* #return BizTV\MediaManagementBundle\Entity\Folder
*/
public function getFolder()
{
return $this->folder;
}
}
And the form:
<?php
namespace BizTV\MediaManagementBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class ImageType extends AbstractType
{
function __construct($createAction=0) {
$this->createAction = $createAction;
}
public function buildForm(FormBuilder $builder, array $options)
{
$createAction = $this->createAction;
if ($createAction) {
$builder
->add('file')
;
}
$builder
->add('name', 'text', array('label' => 'Namn'))
;
}
public function getName()
{
return 'biztv_mediamanagementbundle_imagetype';
}
}
You can't, for security purposes, set a file for the upload field. See here for more info. How to set the value of a HTML file field?
I suggest you are trying to access the file property of your entity in twig. Please take a quick look at your upload function.
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
As you can see the file property is being unset after the upload and persist operation has completed.
Now to have twig show your actual image you have to use the webPath property as this is the generated url to your newly uploaded image.
File uploading can be handled a bit easier with the Dustin10/VichUploaderBundle which also supports file system abstraction with KnpLabs/Gaufrette.
Hope this helps :)

ORM Self-referenced entity not persisting into database properly

ive been trying to figure this out for 2 hours now and i cant seem to understand what went wrong.
I am using Symfony2 and FOSUserBundle.
I created a User entity which extends FOSUserBundle's BaseUser class. Within this User entity, i have 3 variables, id, my_mentors and my_mentees. More details are below:
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="my_mentees")
*/
protected $my_mentors;
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="my_mentors")
* #ORM\JoinTable(name="mentor_and_mentee_relationship",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="mentors_or_mentees_user_id", referencedColumnName="id")}
* )
*/
protected $my_mentees;
public function __construct()
{
parent::__construct();
$this->my_mentors = new ArrayCollection();
$this->my_mentees = new ArrayCollection();
}
public function __toString()
{
return $this->getUsername();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add my_mentors
*
* #param Fitness\FitBundle\Entity\User $myMentors
*/
public function addUser(\Fitness\FitBundle\Entity\User $myMentors)
{
$this->my_mentors[] = $myMentors;
}
/**
* Get my_mentors
*
* #return Doctrine\Common\Collections\Collection
*/
public function getMyMentors()
{
return $this->my_mentors;
}
/**
* Get my_mentees
*
* #return Doctrine\Common\Collections\Collection
*/
public function getMyMentees()
{
return $this->my_mentees;
}
}
I created the self reference because a Mentee(which is a User) will subscribe to a Mentor(which is also a User). I tried to do this using the following function:
public function subscribeAction($menteeID, $mentorID)
{
$em = $this->getDoctrine()
->getEntityManager();
$mentor = $em->getRepository('TestBundle:User')
->find($mentorID);
$mentee = $em->getRepository('TestBundle:User')
->find($menteeID);
$currentMentors = $mentee->getMyMentors();
if ($currentMentors->contains($mentor))
$this->get('session')->setFlash('subscribe-notice', 'You have already signed up to this mentor!');
else
{
$mentee->setIsMentor(false);
$mentee->addUser($mentor);
$mentor->addUser($mentee);
$em->persist($mentee);
$em->persist($mentor);
$em->flush();
$this->get('session')->setFlash('subscribe-notice', 'Subscription succesful!');
}
return $this->redirect($this->generateUrl('TestBundle_testpage', array('id' => $mentor->getMentorProfile()->getId()) ));
}
The problem here is that when i check the database, it does not persist the data. The mentor-mentee relationship information is not stored in the table "mentor_and_mentee_relationship" as declared by the annotation.
I persisted both $mentor and $mentee in an attempt to get it to work, but apparently it doesnt.
Could my ORM annotation be declared wrongly?
You are using the same function (addUser) to add a mentor and to add a mentee. This is wrong. First you need two different setters in your entity (I changed the name of addUser to make it clear)
/**
* Add my_mentors
*
* #param Fitness\FitBundle\Entity\User $myMentors
*/
public function addMentor(\Fitness\FitBundle\Entity\User $myMentors)
{
$this->my_mentors[] = $myMentors;
}
/**
* Add my_mentees
*
* #param Fitness\FitBundle\Entity\User $myMentees
*/
public function addMentee(\Fitness\FitBundle\Entity\User $myMentees)
{
$this->my_mentees[] = $myMentees;
}
Then in your controller do:
$mentee->addMentor($mentor);
$mentor->addMentee($mentee);

Resources