Doctrine2 Conditional Relationship - mysql

My database schema:
LIKE PAGE ARTICLE
id id id
userid name text
itemid
LIKE.itemid -> PAGE.id "or" ARTICLE.id
Normally, i use this way;
Page.php;
/**
* #ORM\OneToMany(targetEntity="Like", mappedBy="page")
*/
protected $likes;
Article.php;
/**
* #ORM\OneToMany(targetEntity="Like", mappedBy="article")
*/
protected $likes;
Like.php;
/**
* #ORM\ManyToOne(targetEntity="Page", inversedBy="likes")
* #ORM\JoinColumn(name="itemid", referencedColumnName="id")
*/
protected $page;
/**
* #ORM\ManyToOne(targetEntity="Article", inversedBy="likes")
* #ORM\JoinColumn(name="itemid", referencedColumnName="id")
*/
protected $article;
But, But, itemid column will be the same in both tables (page and article). Will not be realized.
The question; I want to add a condition. Like this;
LIKE PAGE ARTICLE
id id id
userid name text
itemid
**itemtype**
and
/**
* #ORM\ManyToOne(targetEntity="Article", inversedBy="likes")
* #ORM\JoinColumn(name="itemid", referencedColumnName="id", **condition="itemtype=1"**)
*/
protected $article;
like this way, etc.
What should I do?

You probably want to create a supertype, say Document, from which Article and Page can inherit. Then your Likes can relate to Documents.
Doctrine2 ORM supports three types of inheritance: Mapped Superclass, Single Table, and Class Table. Picking the one you need will require some background on your usage requirements.
Read the Design-time considerations, Performance impact, and SQL Schema considerations of each option on the documentation page linked above to figure out which is right for you.

Related

MYSQL - How to search in JSON array?

On my Symfony 5 app, i've a database with a candidate table that contains a json field.
candidate 1 : [{"end": "30/04/2020", "start": "01/03/2020"},{"end": "31/07/2020", "start": "01/07/2020"}]
candidate 2 : [{"end": "31/03/2020", "start": "01/03/2020"},{"end": "31/07/2020", "start": "01/07/2020"}]
Is it possible with query builder to find a candidate where this field corresponds to the arguments ?
ex: I would like to find all the candidates who are available between 10/03/2020 and 10/04/2020.
This case should just return the candidate 1.
I guess it's not possible to do this with query builder so i'm trying to use native SQL but... what's the sql syntax ?
I tried with availability_dates`->"$.start" = "01/03/2020" but it does not work because it's a "collection".
This is a poorly-conceived database structure. Clearly, the JSON string represents a "repeating group" of related data, which violates the principles of so-called "normal forms."
https://en.wikipedia.org/wiki/Database_normalization
You should be storing the start/end dates in a separate table, say, candidate_dates, with columns like candidate_id, start, end. This has a so-called "one-to-many relationship" to the parent table, candidates.
Now, you can write a simple query which JOINs the two tables to get the answers you need.
Entity like that ?Entity like that ?
One candidate can have one or more available dates and one available dates can only be linked to one candidate.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Table(name="candidate_available_dates", uniqueConstraints={
* #ORM\UniqueConstraint(name="unique_candidate_available_dates", columns={"candidate_id", "start", "end"})
* })
*
* #ORM\Entity(repositoryClass="App\Repository\CandidateAvailableDatesRepository")
*/
class CandidateAvailableDates
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Candidate", inversedBy="candidateAvailableDates")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="candidate_id", referencedColumnName="candidate_id", nullable=false)
* })
*/
private $candidate;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank
*/
private $start;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank
*/
private $end;
[...]
// GETTER and SETTER
And in Candidate entity, the reversed side
/**
* #ORM\OneToMany(targetEntity="App\Entity\CandidateAvailableDates", mappedBy="candidate")
*/
private $candidateAvailableDates;

symfony 2 many to many relation crud

I've created a many to many relation and generated crud via command line. I have Users and Groups.
USER
/**
* #ORM\ManyToMany(targetEntity="Grup", mappedBy="users")
* #ORM\JoinTable(name="user_has_grup",
* joinColumns={#ORM\JoinColumn(name="grup_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
protected $grups;
Grup
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="grups")
* #ORM\JoinTable(name="user_has_grup",
* joinColumns={#ORM\JoinColumn(name="grup_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
protected $users;
When i create user group show up but i can't assign user to group. Still when i go to edit Group i can assign User to it and its works well.
What do I need to change, if i want to be able do it in both directions? Is there any Doctrine Entity change or in controller ?
Don't know if it's your problem because not enough code...
But i think this can help : Symfony2-Doctrine: ManyToMany relation is not saved to database
And official documentation :Owning and Inverse Side on a ManyToMany association

Symfony Doctrine - Joins without foreign keys

I'm trying to set up a schema to capture twitter users and their followers.
I have two tables. TwitterUser and Follower. Follower has 3 fields - id, twitterUser, follower.
When a twitter user is a added to the table, I also add a row to Followers to join them with other users I may be interested in.
However, If I get Symfony/Doctrine to build the tables using something like the following-
/**
* #ORM\Entity
* #ORM\Table(name="follower")
* #ORM\HasLifecycleCallbacks
*/
class Follower
{
public function __construct()
{
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="TwitterUser", inversedBy="followers")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $twitterUser;
/**
* #ORM\ManyToOne(targetEntity="TwitterUser", inversedBy="following")
* #ORM\JoinColumn(name="follower_id", referencedColumnName="id")
*/
protected $follower;
...
It insists on creating a Foreign Key for follower that I don't want, as I don't want to have to get ALL twitter users to ensure that my joins always work.
The only way I can think to do it, is to remove the annotation and create the SQL for the join myself. Is there a smarter way to do it?
I suggest using a ManyToMany self-referencing relationship as describe below:
http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#many-to-many-self-referencing

Symfony2 Repositories and custom joins

Ok i have a couple of tables in a database that I CAN NOT change the the structure to. That being said what I am trying to accomplish is to have a controller interact with the entities, do a custom join and return the results.
MORE DETAILS:
1st table has id, username
2nd table has user_id, stat1, stat2, stat3
what i want is to search for all users in table 1 joining table 2. I can do this with straight MySQL pretty easy but i want to know how to do it the symfony way.
You need to tell Doctrine where to find each piece of information so that it can load all the properties every time you instantiate a new User object. In other words, you need to add custom Doctrine mapping information. Assuming you're adding your mapping information as inline annotations to your User's model class, the code would look something like this:
//in User.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="first_table_name")
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $username;
/**
* #ORM\OneToMany(targetEntity="UserStats")
*/
protected $stats;
//also define getters and setters for the above, of course
}
//in UserStats.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="table_two_name")
*/
class UserStats
{
/**
* I'm pretty sure doctrine will require that you add an Id column to table_two,
* which is what this is. If you can't add an Id, I'm not sure it'll work...
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* The below assumes your stats are strings. If not, change the type attribute.
* #ORM\Column(type="string")
*/
protected $stat1;
/**
* #ORM\Column(type="string")
*/
protected $stat2;
/**
* #ORM\Column(type="string")
*/
protected $stat3;
//include appropriate getters/setters here too
}

Doctrine 2 with multiple indexes

I am developing using zend framework and doctrine2.1.
I have generated entities from database.
But the problem is: Doctrine doesn't recognize my indexes. They are not marked in entity annotations at all.
And when I go to validate-schema and dump sql from orm:schema-tool:update --dump-sql it generates sql to drop all my indexes across whole database.
I found that Doctrine has following annotation used for defining indexes:
indexes={#index(name="index_name",
columns={"database_column1","database_column2"}
)}
But this allows me to define one index for multiple columns and I don't really need that.
What I want is the ability to define multiple indexes on multiple columns, one index per column.
Is there a way I can achieve this? Is there a way that I can have annotation that defines multiple indexes.
I would say you can insert multiple indexes in the indexes property (but I haven't had the time to test it):
indexes={
#ORM\Index(name="index_name", columns={"database_column1","database_column2"}),
#ORM\Index(name="index_name2", columns={"database_column1"}),
#ORM\Index(name="index_name3", columns={"database_column2"})
}
Hope this helps you
Here is an example:
/**
* #Entity
* #Table(name="serial_number",indexes={
* #index(name="product_idx", columns={"product_id"}),
* })
*/
class SerialNumber { // Entity Class
/**
* #var int
*
* #Id
* #GeneratedValue
* #Column(type="integer")
*/
protected $id;
/**
* #Column(name="created_at", type="datetime")
* #var \DateTime
* */
protected $created;
/**
* #Column(name="updated_at", type="datetime")
* #var \DateTime
* */
protected $updated;
/**
* #Column(name="product_id", type="integer")
*/
protected $productID;
}