making unique Constraint on doctorine2 - symfony

I would like to make uniqueConstraint user and lesson.
/**
* #ORM\Table(name="ReviewSchool",uniqueConstraints={
* #ORM\UniqueConstraint(name="lessonid", columns={"lesson", "user"})})
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class ReviewSchool
{
* #ORM\ManyToOne(targetEntity="Lesson",inversedBy="reviewschool")
* #ORM\JoinColumn(name="review_lesson", referencedColumnName="id",onDelete="cascade")
*/
private $lesson;
/**
*
* #ORM\ManyToOne(targetEntity="User",inversedBy="reviewschool")
* #ORM\JoinColumn(name="review_user",referencedColumnName="id",onDelete="cascade")
*/
private $user;
However it shows
[Doctrine\DBAL\Schema\SchemaException]
There is no column with name 'lesson' on table 'ReviewSchool'.
Surely I have 'lesson' column, how can I solve this?
I have misunderstood something??

It allows to hint the SchemaTool to generate a database unique constraint on the specified table columns. It only has meaning in the SchemaTool schema generation context.
So you have to use column names. In your case:
#ORM\UniqueConstraint(columns={"review_lesson", "review_user"})}

Related

Use Doctrine to merge entity with different subclass

I have a mapped superclass AbstractQuestion with single-table-inheritance.
/**
* #ORM\Entity
* #ORM\MappedSuperclass
* #ORM\Table(name="Question")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="dtype", type="string")
* #ORM\DiscriminatorMap({
* "simple": "SimpleQuestion",
* "dropdown": "DropdownQuestion"
* })
*/
abstract class AbstractQuestion
SimpleQuestion and DropdownQuestion inherit from this superclass.
/**
* Class SimpleQuestion.
* #ORM\Entity()
*/
class SimpleQuestion extends AbstractQuestion
I want to modify an existing SimpleQuestion and make it a DropdownQuestion.
When saving a question, I deserialise and merge the question, which contains an ID and the 'dtype' and other properties.
$dquestion = $this->serial->fromJson($request->getContent(), AbstractQuestion::class);
$question = $this->em->merge($dquestion);
$this->em->flush();
So I submit something like:
{ id: 12, dtype: 'dropdown', 'text': 'What is my favourite animal?'}
After the deserialisation, $dquestion is a DropdownQuestion object as I desired, but after the merge $question is a SimpleQuestion object as it was in the database previously, so any unique properties of DropdownQuestion are lost and the question is saved as a SimpleQuestion. Is there any way to work around this?
You will first have to delete the existing record (SimpleQuestion) and then insert the new record (DropdownQuestion). Type casting is not supported in Doctrine 2.
Note.
You can probably change the discriminator column with a pure SQL query, but this is absolutely not recommended and will for sure give you problems...
Check also the answers here and here since they might be interesting for you.

Doctrine: authorize NULL in a foreign composite key

I have the following entity:
/**
* SeriesAuthorRole
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Blog\Bundle\CoreBundle\Entity\SeriesAuthorRoleRepository")
*/
class SeriesAuthorRole extends AuthorRoleAbstract
{
/**
* #var Series
*
* #ORM\ManyToOne(targetEntity="Blog\Bundle\CoreBundle\Entity\Series", inversedBy="authors")
* #ORM\JoinColumn(name="series", referencedColumnName="id", nullable=false)
* #ORM\Id
*/
private $series;
/**
* #var Author
*
* #ORM\ManyToOne(targetEntity="Blog\Bundle\CoreBundle\Entity\Author")
* #ORM\JoinColumn(name="author", referencedColumnName="id", nullable=false)
* #ORM\Id
*/
protected $author;
/**
* #var Role
*
* #todo Must be nullable
*
* #ORM\ManyToOne(targetEntity="Blog\Bundle\CoreBundle\Entity\Role")
* #ORM\JoinColumn(name="role", referencedColumnName="id", nullable=true)
* #ORM\Id
*/
protected $role;
// ... Getters, setters
}
The idea behind it is quite simple: We have author, role and series entities. A series can have several authors with various roles. A same author can fulfill multiple roles in a series.
Sometimes, we don't know exactly what was the role of the author. In this case, the NULL value will be used for the role, the NULL value standing for "I don't know".
I was taught not to use NULL in foreign composite keys unless it has meaning. Well, it has meaning here, and I know that this could be implemented without Doctrine. However, for now, Symfony 2 throws that error:
Entity of type Blog\Bundle\CoreBundle\Entity\BandAuthorRole is missing an assigned ID for field 'role'. The identifier generation strategy for this entity requires the ID field to be populated before EntityManager#persist() is called. If you want automatically generated identifiers instead you need to adjust the metadata mapping accordingly.
500 Internal Server Error - ORMException
So how can I authorize NULL values in foreign composite keys ? Is it possible at all with Doctrine ?
Your #JoinColumn annotation is correct with referencing to http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#annref-joincolumn
However,
Every entity with a composite key cannot use an id generator other
than “ASSIGNED”. That means the ID fields have to have their values
set before you call EntityManager#persist($entity).
http://docs.doctrine-project.org/en/2.0.x/tutorials/composite-primary-keys.html#general-considerations

Shorthand for creating indexes on all columns

In Doctrine2, I can write the following if I want to create single-column indexes for a set of columns in an entity:
/**
* Project
*
* #ORM\Entity(repositoryClass="Company\SomeBundle\Entity\ProjectRepository")
* #ORM\Table(indexes={
* #ORM\Index(name="name_idx", columns={"name"}),
* #ORM\Index(name="started_idx", columns={"started"}),
* })
*/
However, what if I wanted to create an index for each column in this entity? Is there a shorthand for this, so that I don't have to write it explicitly for each column like in the above example?
Just list properties that should be indexed.
<?php
/**
* #Entity
* #Table(name="ecommerce_products",indexes={#index(name="search_idx",columns={"name","email"})})
*/
class ECommerceProduct
{
}

Doctrine2 - How can i make a relationship with string column

I having a following issue, I need to make a relationship with two tables, but with no regular id, i need to use strings column. Something like this:
/**
* #ORM\Entity
* #ORM\Table(name="sigtap_tb_procedimento")
*/
class Procedimento
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="ExcecaoCompatibilidade", mappedBy="procedimento_restricao")
* #ORM\JoinColumn(name="co_procedimento_restricao", referencedColumnName="co_procedimento")
*/
private $restricoes;
}
And another Entity
/**
* #ORM\Entity
* #ORM\Table(name="sigtap_rl_excecao_compatibilidade")
*/
class ExcecaoCompatibilidade
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Procedimento", inversedBy="restricoes")
* #ORM\JoinColumn(name="co_procedimento_restricao", referencedColumnName="co_procedimento")
*/
private $procedimento_restricao;
}
co_procedimento_restricao and co_procedimento_restricao are string type, The relation does not working. How can i solve this issue?
Your relation needs to reference a primary key in the other table.
May be I misunderstood your question but cant you reference the id collumn the relationship like this:
/**
* #ORM\OneToMany(targetEntity="ExcecaoCompatibilidade", mappedBy="procedimento_restricao")
*/
private $restricoes;
/**
* #ORM\ManyToOne(targetEntity="Procedimento", inversedBy="restricoes")
* #ORM\JoinColumn(name="co_procedimento_restricao", referencedColumnName="id")
*/
private $procedimento_restricao;
Take a look here:
http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html
using one-to-many relations with doctrine
The side using #OneToMany is always the inverse side of a relation from doctrine's pov ( possibly not what you consider being the inverse side ) and never has a join-column definition.
Remove the #JoinColumn annotation from class Procedimento.
#OneToMany has to use mappedBy and #ManyToOne (the owning side) uses inversedBy.
The join-column (or join-table) definition has to be on the owning side together with #ManyToOne.
When using a join-column the name of this column (which will be added to the table of the owning side entity aka the side being "many") will be specified by name="column_name" and the referenced foreign key to store in there is the referencedColumnName="id"definition of the #JoinColum annotation.

JMSSerializerBundle serialization groups in entities with relations

I have a problem with serializing entity with many relations using groups.
I have a problem with serializing related entities this way.
Let's say I have two entities: Product and related Element.
/**
*
* #Serializer\ExclusionPolicy("none")
*/
class Product {
/**
* Primary key
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Serializer\Groups({"list","details"})
* #Serializer\Type("integer")
*/
protected $id;
/**
* #Serializer\Groups({"list","details"})
* #Serializer\Type("string")
*/
protected $name;
/**
* #ORM\Column(name="description", type="string", length=4096, nullable=true)
*
* #Serializer\Groups({"details"})
* #Serializer\Type("string")
*/
protected $description;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Madden\ProjectBundle\Entity\ProjectResource", mappedBy="project")
* #Serializer\Groups({"details"})
* #Serializer\Type("ArrayCollection<Element>")
*/
protected $details1;
/**
* Relation to project tasks
* #ORM\OneToMany(targetEntity="Madden\ProjectBundle\Entity\ProjectTask", mappedBy="project")
* #Serializer\Exclude()
* #Serializer\Type("ArrayCollection<Element>")
*/
protected $details2;
...
}
Element entity has a similar structure:
/**
*
* #Serializer\ExclusionPolicy("none")
*/
class Element {
/**
* Primary key
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Serializer\Groups({"list","details"})
* #Serializer\Type("integer")
*/
protected $id;
/**
* #Serializer\Groups({"list","details"})
* #Serializer\Type("string")
*/
protected $name;
/**
* #ORM\Column(name="description", type="string", length=4096, nullable=true)
*
* #Serializer\Groups({"details"})
* #Serializer\Type("string")
*/
protected $description;
...
}
My problem is that when I'm serializing Product with 'details' group entity I want to serialize only id's of Elements but as you see entity has defined same groups as Product (in case that I would need details of element object) because I want have unified groups on all my entities and prevent making hundreds of groups like 'product_details','element_details', and so on.
Is there a way to eventualy change serialization group when I visit relation or something like that? Handler maybe or something like that?
Regards and thanks for any help
Unfortunately, you can't really (but keep reading ;-)), well at least not without changes to the serializer library. The culprit is that the list of groups is fixed within a GroupExclusionStrategy (which is referenced by the Context) the minute you start the serialization process. There is actually an assertion within the code that prevents modification of the exclusion strategy once the (de-)serialization is running.
But as it happens, I had the exact same problem in a project of mine as well, and I hacked the necessary changes into the serializer code. I have cleaned the code up a bit and uploaded it to Github (https://github.com/andreasferber/serializer/tree/recursion-groups).
It adds new property metadata with which you can add, remove or override the groups when descending into subobjects. With annotations it looks like this:
/**
* #Serializer\RecursionGroups(set={"foo", "bar"}, add={"baz"}, remove={"Default"})
*/
private $myProperty;
You should be able to use XML or Yaml metadata as well, however this is untested since I don't use them and I haven't added test cases yet. Have a look at the reference documentation. Since I haven't done any optimizations yet either, if your entities are really large and deeply nested, it might have a noticable performance impact.
Please let me know if you find this useful, or if you have any suggestions, because if this isn't only needed by me, I will add some tests and try to submit it upstream.
A solution for this is actually described in the official documentation.
That being said the solution proposed by #aferber seems better on many points: easier to maintain, less verbose, more flexible...
You need to use setGroups.
The _group suffix used in the official documentation is not needed.
$context->setGroups([
'Default', //if you want
// use this linked entity but show only its id
'group_of_linked_field',
'group_of_linked_field' => [
'id' // you will need to define this group first
],
// use this linked entity and show fields as described
'group_of_other_linked_field',
'group_of_other_linked_field' => [
// just as an example
'Default',
'details',
],
]);
This does not work with addGroup or addGroups! Both of them won't accept associative arrays. setGroups is your (only?) solution.

Resources