Inheritance issue in symfony2 and doctrine2 - symfony

I have many ads entities (MotorAds, RealestateAds, ElectronicsAds, ...) that share some attributes like title and description. In order to avoid redefining these attributes for each Ads entity, one can use the mapped superclass methods as follows:
<?php
/** #MappedSuperclass */
class MappedSuperclassAds{
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255, nullable=false)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=false)
*/
private $description;
}
Then, the inheritance will do the job.
Now, what is the problem? The problem is that each Ads entity is related to its entity that defines the list of users that added the ads to their favorites. To do that (the MotorsAds entity for example),
1.linking the MotorsAds entity to its MotorsFavorite entity through that code:
/**
* #ORM\OneToMany(targetEntity="Minn\AdsBundle\Entity\MotorsFavorite",
* mappedBy="motors",cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $favorites;
2.Defining the MotorsFavorite entity as fellows:
<?php
namespace Minn\AdsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* MotorsFavorite
*
* #ORM\Table(
* uniqueConstraints={#ORM\UniqueConstraint(name="unique_fav_motors",
* columns={"user_id", "motors_id"})})
* #ORM\Entity(repositoryClass="Minn\AdsBundle\Entity\MotorsFavoriteRepository")
* #ORM\HasLifecycleCallbacks()
*/
class MotorsFavorite {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Minn\UserBundle\Entity\User")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Minn\AdsBundle\Entity\MotorsAds", inversedBy="favorites")
* #ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $motors;
//...
}
As you can see, the linkage between the MotorAds and MotorFavorite is a hard linkage, which means that I have to create a Favorite entity for each Ads entity I create (FavoriteMotors, FavoriteRealestate, FavoriteElectronics, ...). This is a long and repetitive work.
So my question is:
1.Creating a super mapped class called SuperMappedFavorite which will only include the $id and $user attributes will reduce the repetitive work. But what about the the attribute $motors? $motors is hardly linked to the entity MotorsAds as you see here:#ORM\ManyToOne(targetEntity="Minn\AdsBundle\Entity\MotorsAds", inversedBy="favorites"). All the burden of the work is in the setters and getters of $motors.
2.Is it possible to make the target entity an interface like this:
<?php
// SuperMappedFavorite.php
// ...
#ORM\ManyToOne(targetEntity="Minn\AdsBundle\Favorite\FavoriteAwareInterface", inversedBy="favorites")
private $object;
// ...
and the MotorsAds entity will be implementing in this the FavoriteAwareInterface
If anyone has a good link/article regarding this kind of issue, I will be happy to have it.
Thanks.

Yes, you can set an interface as target entity, as described in the Symfony documentation.
The process is basically:
defining the interface (your Minn\AdsBundle\Favorite\FavoriteAwareInterface),
setting the interface in the parent entity (as you already did),
implementing the interface in a different entity (would be class MotorsFavorite implements FavoriteAwareInterface) – and yes, it can also be derived from a mapped superclass,
and then telling Doctrine to use your implementation through the doctrine.orm.resolve_target_entities config parameter.
See the documentation for details and a code example.

Related

Symfony 4 - UniqueEntity : I want that several companies may use the same name for a service but a company can't have two services with the same name

in my symfony project I have a groupValidators entity that is used to create a service within a company by indicating the name of the service and users...
GroupeValidateurs.php:
<?php
namespace App\Entity;
use Cocur\Slugify\Slugify;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupeValidateursRepository")
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity(
* fields = {"nom"},
* message = "Ce nom a déjà été utilisé"
* )
*/
class GroupeValidateurs
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* Nom du service
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $nom;
/**
* Liste des utilisateurs membres du service
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="groupe")
*/
private $users;
/**
* #ORM\Column(type="string", length=255)
*/
private $slug;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Validateur", mappedBy="groupeValidateurs", orphanRemoval=true, cascade={"persist", "remove"})
* #ORM\OrderBy({"ordre" = "ASC"})
*/
private $validateurs;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="services")
* #ORM\JoinColumn(nullable=true)
*/
private $entreprise;
And I want a company to be unable to enter the same service name twice (hence my #UniqueEntity), but I still want more than one company to use the same name for a service. For example company A can have a service called "Commerce", and company B too.
How could I do that? Because currently, if a company tries to create a service with a name already used for the service of another company, I have my constraint which intervenes
Assuming you have the id of the company and the name of the service in this entity, you can use unique constraint on two columns in your table definition:
/**
*
* #Table(name="GroupeValidateurs",
* uniqueConstraints={
* #UniqueConstraint(name="service_entreprise",
* columns={"service", "entreprise"})
* }
* )
*/
You should take care when you use french in your class name. People might think here you are speaking about Validator, which is not the case. I suggest you to rename your class GroupeValidateurs to something more speaking like Division or Department or Service. Also when you use a class name use singular.
What you need to do is add a constraint to ensure you don't have many times the same service name. You did that part correctly with your annotation.
Then you want :
One company has one service
One service can be related to many companies
So your problem is here
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="services")
* #ORM\JoinColumn(nullable=true)
*/
private $entreprise;
You need to use OneToMany here as one service can be related to many companies
EDIT: I might have missread your question I didn't saw you seems to want a company can have not one service but two services. For this part we need you to provide your Company class.

Symfony2 OnetoOne relations

I have two class, User and PersonalData. We need relation OnetoOne with Doctrine in Symfony2. In my code I tried this relations, but in MySQL doesnt appear the foreign key.
My code:
namespace TFC\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*/
/** #ORM\Entity */
class User
{
/**
* #Id #Column(type="integer") #GeneratedValue
*/
private $id;
/**
* #var string
*/
private $email;
}
use Doctrine\ORM\Mapping as ORM;
/**
* PersonalData
*/
/** #ORM\Entity */
class PersonalData
{
/** #Id #OneToOne(targetEntity="User") */
private $userId;
/**
* #var string
*/
private $firstName;
}
You have to prefix with #ORM all your annotations like this:
#ORM\Id
#ORM\Column(type="integer")
#ORM\GeneratedValue(strategy="AUTO")
This "Unidirectional association" needs to be placed on User entity (because I think you want to load that informations from the User object...) and you don't need to care about the foreign key because doctrine create it automatically.
So place a $personalData property on User Entity and apply there the association, then delete $userId from PersonalData and add the $id property like did in User.
Even if the documentation claims that is not necessary have you tried something like:
#JoinColumn(name="userId", referencedColumnName="id")
When you generate the entity do you get any message?

How to Inject Logged User into Property by Annotation

I have an entity called Item as above:
<?php
/**
* Item
* #ORM\Table(name="item")
* #ORM\Entity
*/
class Item {
/**
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Acme\UserBundle\Entity\User
* ???
*/
private $user;
.....
How can I inject logged User object into $user property via annotation in the easiest way?
If you are trying to save the created/updated timestamps and the user who performed these actions with your entity ... this is called blameable and timestampable behavior!
Have a look at Knp\DoctrineBehaviors including blameable and timestampable behaviors. (PHP 5.4+ needed)
Gedmo\DoctrineExtensions also provides blameable and timestampable. (PHP >=5.3.2)

How to avoid dependency with entities from different bundles

I have several bundles in my app and I would like to have relations between tables.
One is my User(StoreOwner) which is in UserBundle, and the second is Store in StoreBundle.
The relation between them is OneToMany (User -> is owner of -> Store).
Store
/**
* Description of Store
*
* #ORM\Table(name="Store")
* #ORM\Entity(repositoryClass="Traffic\StoreBundle\Repository\StoreRepository")
* #author bart
*/
class Store extends StoreModel {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $name
*
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(
* message="Please provide your shop name"
* )
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\StoreOwner", inversedBy="stores")
*
*/
protected $owner;
}
StoreOwner
/**
* #ORM\Entity
*
*/
class StoreOwner extends User implements StoreOwnerInterface {
/**
* #var type ArrayCollection()
*
* #ORM\OneToMany(targetEntity="Traffic\StoreBundle\Entity\Store", mappedBy="owner", cascade={"persist"})
*/
protected $stores;
}
My question is:
Is there any solution to avoid dependency between StoreBundle and UserBundle and keep relations between Entities in Doctrine?
This is a valid concern in my opinion. Two-way dependencies between bundles are a smell.
One way of solving the dependency issue is moving your entities out of the bundles into a more general namespace. This way both bundles will depend on the same "library" but won't depend on each other directly.
I recently wrote a blog post on how to do it: How to store Doctrine entities outside of a Symfony bundle?

Single Table Inheritance - discriminator map

I am needing to make a formula to DiscriminatorMap in my class, because I have a lot of class, and I can't discrimine each one.
The discr can be the name of the class.
it's possible? (with annotation, xml or other)
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"MidUpperArmCircumference" = MidUpperArmCircumference", "KneeHeight" = "KneeHeight"})
*/
thanks.
Look this link maybe it'll help you.
https://medium.com/#jasperkuperus/defining-discriminator-maps-at-child-level-in-doctrine-2-1cd2ded95ffb
I simply left out the DiscriminatorMap annotation and Doctrine automatically used the chield's class name as a discriminator:
/**
* #ORM\Entity()
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
*/
abstract class AbstractContent
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
}
/**
* #ORM\Entity()
*/
class Page extends AbstractContent
{
}
Now when I create a new Page() Doctrine creates an AbstractContentand a Page with an FK to the AbstractContent and sets the AbstractContent's type attribute to page.
This perfect as it let's you generate as many subclasses as you like even in other Bundles without your Superclass (in my case AbstractContent) needing to know about them.
But keep in mind that so far this isn't officially documented. Tested with Doctrine ORM 2.3.
This is an old question. Doctrine supports single table inheritance pretty well.
The below example is from official docs
<?php
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/**
* #Entity
*/
class Employee extends Person
{
// ...
}
Read more about it here

Resources