Symfony 5 - Doctrine - Index not created in custom ManyToOne relation - doctrine

I've created two Entity, and i try to create a custom relation between them, without using the default syntax.
See :
/**
* #ORM\Entity(repositoryClass=LandRepository::class)
* #ORM\HasLifecycleCallbacks()
*/
class Land
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $libelle;
/**
* #ORM\Column(type="integer")
*/
private $uid;
/**
* #ORM\OneToMany(targetEntity=Ride::class, mappedBy="uidparent")
*/
private $rides;
}
/**
* #ORM\Entity(repositoryClass=RideRepository::class)
* #ORM\HasLifecycleCallbacks()
*/
class Ride
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $libelle;
/**
* #ORM\ManyToOne(targetEntity=Land::class, inversedBy="rides")
* #ORM\JoinColumn(name="uidparent", referencedColumnName="uid")
*/
private $uidparent;
}
Tables are correctly created, but the last instruction have an error.
In MySQL, i made some test, and i need to create an index on "uid" column in "land" table.
So, i changed the header of my class "Land" to force the index
/**
* #ORM\Entity(repositoryClass=LandRepository::class)
* #ORM\Table(name="land",indexes={#ORM\Index(columns={"uid"})})
* #ORM\HasLifecycleCallbacks()
*/
class Land
{
/ ... /
}
I don't understand why i need to specify this index creation.
I hope to have something like this :
(PS : I know i can use the classic syntax (using in my entity Ride a column auto named "land_id") but I want to understand and master this alternative syntax to manage future complex entities and associations)
Thanks !

Need to manually specify in Entity header annotation :
#ORM\Table(name="land",indexes={#ORM\Index(columns={"uid"})})

Related

orphanRemoval=true removes all related entities

I have a strange problem with a relation between two entities:
One Joboffer can have many jobofferLocations, while Many jobOfferlocations only have one joboffer:
class Joboffer
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id_joboffer", type="integer", length=255, nullable=false)
* #Groups({"api_read", "api_write"})
*/
protected $id;
/**
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Joboffer\JobofferLocation", mappedBy="joboffer", orphanRemoval=true, cascade={"persist"})
* #Groups({"api_read", "api_write"})
* #var ArrayCollection
*/
protected $jobofferLocations;
....
/**
* #param JobofferLocation $jobofferLocation
*/
public function addJobofferLocation(JobofferLocation $jobofferLocation)
{
if ($this->jobofferLocations->contains($jobofferLocation)) {
return;
}
$this->jobofferLocations->add($jobofferLocation);
$jobofferLocation->setJoboffer($this);
}
The jobofferlocationclass:
class JobofferLocation
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id_joboffer_location", type="integer", length=255, nullable=false)
* #Groups({"api_read"})
*/
protected $id;
/**
* #return mixed
*/
public function getJoboffer()
{
return $this->joboffer;
}
/**
* #param mixed $joboffer
*/
public function setJoboffer($joboffer)
{
$this->joboffer = $joboffer;
}
On Updates I have this problem:
When I use "orphanRemoval=true,", it removes all jobofferlocation entities, when I don't use it, but "cascade=remove", it doesn't remove the ones that aren't in the relations any more.
So, is there a way to update all relations? (Remove the ones that aren't needed any more, adding new ones and updating existing ones.)
I found an answer:
first of all, the methods addJObofferLocation and removeJobofferLocation are needed and orphanRemoval must be set to true.
The trick seems to be in adding the right (not double) locations.
class Joboffer
{
...
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Joboffer\JobofferLocation", mappedBy="joboffer", orphanRemoval=true,cascade={"persist"})
* #Groups({"api_read", "api_write"})
* #var ArrayCollection
*/
protected $jobofferLocations;
/**
* #param JobofferLocation $jobofferLocation
*/
public function addJobofferLocation(JobofferLocation $jobofferLocation)
{
if ($this->jobofferLocations->contains($jobofferLocation)) {
return;
}
/** #var JobofferLocation $location */
foreach ($this->jobofferLocations as $location){
//check if this location exists
// it seems we need this, because of the API plattform bundle
if ($location->getIdLocation() == $jobofferLocation->getIdLocation()){
// if it exists, just copy the new jobofferlocation settings
return;
}
}
$jobofferLocation->setJoboffer($this);
$this->jobofferLocations->add($jobofferLocation);
}
public function removeJobOfferLocation(JobofferLocation $jobofferLocation)
{
if (!$this->jobofferLocations->contains($jobofferLocation)) {
return;
}
$this->jobofferLocations->removeElement($jobofferLocation);
$jobofferLocation->removeJobOffer();
}

Symfony ManyToMany relation add constraint without entity

I have ManyToMany relation, without entity and I want add constraint for ManyToMany table, example for uniq fields, how it's done ?
My constrait example for entity
* #ORM\Table(name="courses",
* uniqueConstraints={
* #UniqueConstraint(name="unique",
* columns={"courses_id", "courses_of_study_id"})
* }
* )
*/
class Courses
But this is approcah for entity/ In my case I don't need create entity, just relation ManyToMany in both side. This relation create table in database with courses_courses_of_study with courses_id and courses_of_study_id and I want add to it uniqueConstraints
may entity
class Courses
{
use TraitTimestampable;
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection|CoursesOfStudy[]
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\CoursesOfStudy", inversedBy="courses", cascade={"persist", "remove"})
*/
private $coursesOfStudy;
class CoursesOfStudy
{
use TraitTimestampable;
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection|Courses[]
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Courses", mappedBy="coursesOfStudy", cascade={"persist"})
*/
private $courses;
I try added this uniqueConstraints to entity Courses, but after migration diff I have error
[Doctrine\DBAL\Schema\SchemaException]
There is no column with name 'courses_id' on table 'courses'.
when I try
* #ORM\Table(name="courses")
* #ORM\Table(name="courses_courses_of_study",
* uniqueConstraints={
* #UniqueConstraint(name="unique",
* columns={"courses_id", "courses_of_study_id"})
* }
* )
*/
class Courses
have error
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'project.courses_courses_of_study' already exists.

Symfony3 Edit Entity : Error Missing value for primary key

I try to create a tree structure for a catalog of products.
A catalog can have multiple levels and levels can contain multiple products.
I manage to save my structure in database but when I want to edit it, I have this error :
Error : Missing value for primary key catalogCode on AppBundle\Entity\CatalogLevel
at OutOfBoundsException ::missingPrimaryKeyValue ('AppBundle\Entity\CatalogLevel', 'catalogCode')
in vendor\doctrine\common\lib\Doctrine\Common\Proxy\AbstractProxyFactory.php at line 125
when I do this in my CatalogController :
$form = $this->createForm(CatalogTreeType::class, $catalog);
But, just before that line, I verify if I get my levels correctly and it's looking like that's the case :
// Create an ArrayCollection of the current levels
$originalLevels = new ArrayCollection();
foreach ($catalog->getLevels() as $level) {
var_dump($level->getCatalogCode());
$originalLevels->add($level);
}
// returns
AppBundle\Controller\CatalogController.php:337:string 'TT-FTEST' (length=8)
AppBundle\Controller\CatalogController.php:337:string 'TT-FTEST' (length=8)
CatalogLevel entity has a composite key : levelId + catalogCode.
Considering the primary key catalogCode isn't empty, I don't understand this error...
Catalog Entity
/**
* #ORM\Table(name="catalogue")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CatalogRepository")
* #UniqueEntity(fields="code", message="Catalog code already exists")
*/
class Catalog
{
/**
* #ORM\Column(name="Catalogue_Code", type="string", length=15)
* #ORM\Id
* #Assert\NotBlank()
* #Assert\Length(max=15, maxMessage="The code is too long ({{ limit }} characters max)")
*/
private $code;
/**
* #ORM\OneToMany(targetEntity="CatalogLevel", mappedBy="catalog", cascade={"persist", "remove"})
* #Assert\Valid
*/
private $levels;
/**
* Constructor
*/
public function __construct()
{
$this->levels = new ArrayCollection();
}
/**
* Get levels
*
* #return ArrayCollection
*/
public function getLevels()
{
return $this->levels;
}
/**
* Add level
*
* #param \AppBundle\Entity\CatalogLevel $level
*
* #return Catalog
*/
public function addLevel(\AppBundle\Entity\CatalogLevel $level)
{
$level->setCatalogCode($this->getCode());
$level->setCatalog($this);
if (!$this->getLevels()->contains($level)) {
$this->levels->add($level);
}
return $this;
}
/**
* Remove level
*
* #param \AppBundle\Entity\CatalogLevel $level
*/
public function removeLevel(\AppBundle\Entity\CatalogLevel $level)
{
$this->levels->removeElement($level);
}
}
CatalogLevel Entity
/**
* #ORM\Table(name="catalogue_niveau")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CatalogLevelRepository")
*/
class CatalogLevel
{
/**
* #ORM\Column(name="Niveau_ID", type="string", length=15)
* #ORM\Id
*/
private $id;
/**
* #ORM\Column(name="Catalogue_Code", type="string", length=15)
* #ORM\Id
*/
private $catalogCode;
/**
* #ORM\ManyToOne(targetEntity="Catalog", inversedBy="levels")
* #ORM\JoinColumn(name="Catalogue_Code", referencedColumnName="Catalogue_Code")
*/
private $catalog;
/**
* Set id
*
* #param string $id
*
* #return CatalogLevel
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* Set catalogCode
*
* #param string $catalogCode
*
* #return CatalogLevel
*/
public function setCatalogCode($catalogCode)
{
$this->catalogCode = $catalogCode;
return $this;
}
/**
* Get catalogCode
*
* #return string
*/
public function getCatalogCode()
{
return $this->catalogCode;
}
}
I would like to remind you that this error occured on the editAction (it works very well on the addAction) when I display the pre-filled form.
Thanks for your help !
I think that is because you haven't autoincrement id in entity CatalogLevel. Try add to id this code:
#ORM\GeneratedValue(strategy="AUTO")
You have some problems in the way you've created you Entities. You should use auto generate strategy. Also the "#ORM\Id" annotation is the unique identifier.
Also, your "JoinColumn" is incorrect. You need to refer back to the "Catalog" Entity, and it's id (identifier). There is no need for 2 "#ORM\Id" entries in the class CatalogLevel.
So make these changes:
/**
* #ORM\Table(name="catalog")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CatalogRepository")
*/
class Catalog
{
/**
* #ORM\Column(name="cat_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $cat_id;
/**
* #ORM\OneToMany(targetEntity="CatalogLevel", mappedBy="catalog", cascade={"persist", "remove"})
* #Assert\Valid
*/
private $levels;
...
/**
* #ORM\Table(name="catalog_level")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CatalogLevelRepository")
*/
class CatalogLevel
{
/**
* #ORM\Column(name="cat_level_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $cat_level_id;
/**
* #ORM\ManyToOne(targetEntity="Catalog", inversedBy="levels")
* #ORM\JoinColumn(name="local_cat_id", referencedColumnName="cat_id")
*/
private $catalog;
...

Many-to-Many Relations using non-"id" Primary Key in Doctrine

So I'd like to create two entities and make a many-to-many reference. I would love to make this association using a string primary key on one table. This seems to be really hard, at least it took me pretty much time trying without any results yet.
This is my approach:
First entity:
namespace Project\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="User")
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $Id;
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="Users")
* #ORM\JoinTable(name="role_user",
* joinColumns={#ORM\JoinColumn(name="User_Id", referencedColumnName="Id")},
* inverseJoinColumns={#ORM\JoinColumn(name="Role_Name", referencedColumnName="Name")}
* )
*/
private $Roles;
}
And the second:
namespace Project\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="Role")
*/
class Role
{
/**
* #ORM\Id
* #ORM\Column(type="string", length=256)
*/
private $Name;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="Roles")
* #ORM\JoinTable(name="role_user",
* joinColumns={#ORM\JoinColumn(name="Role_Name", referencedColumnName="Name")},
* inverseJoinColumns={#ORM\JoinColumn(name="User_Id", referencedColumnName="Id")}
* )
*/
private $Users;
}
Output of ./app/console doctrine:schema:validate:
[Mapping] FAIL - The entity-class 'Project\AdminBundle\Entity\User' mapping is invalid:
* The referenced column name 'Id' has to be a primary key column on the target entity class 'Project\AdminBundle\Entity\User'.
* The referenced column name 'Id' has to be a primary key column on the target entity class 'Project\AdminBundle\Entity\Role'.
What do I miss?
Attention upper/lowercase! Doctrine generates its columns as lowercase per default. This solves the issue:
* joinColumns={#ORM\JoinColumn(name="User_Id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="Role_Name", referencedColumnName="name")}
This it over-complicated; it's enough to put this into the Role entity:
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="Roles")
* #ORM\JoinColumn(name="role_user", referencedColumnName="name")
*/
private $Users;
I only thought about this when writing the question.. what a good method to think about the problem again from scratch.
Cheers

FOSElasticaBundle to filter out entity with certain property

I have setup FOSElasticaBundle that indexes InstagramShopPictures, which has the following property:
class InstagramShopPicture
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #Exclude()
* #ORM\OneToMany(targetEntity="MyApp\MainBundle\Entity\InstagramPictureCategory", mappedBy="picture", cascade={"persist","remove"})
*/
protected $category;
}
How do I specify in elasticsearch (or FOSElasticaBundle) that I want to filter out the results such that only the one that has category of A, where A is a InstagramPictureCategory that the user specifies. Is this possible to filter this out in elastica?

Resources