Symfony Doctrine runs same queries - mysql

since the last update of my server which happened last month, I face an unusual problem with Doctrine.
When I run doctrine:schema:update --force, I the result "54 queries executed". The same result happens even if I don't change my entities.
When I run doctrine:schema:update --dump-sql to see the queries, I can see that same queries are run. Ex :
ALTER TABLE artisan CHANGE creator_id creator_id INT DEFAULT NULL;
ALTER TABLE user_connection CHANGE date date DATETIME DEFAULT NULL;
ALTER TABLE device CHANGE device_type device_type VARCHAR(500) DEFAULT 'ios' NOT NULL;
The problem is that columns are from the same type that the one changed by the query. I don't know why Doctrine want to change the type.
MariaDB version is 10.4.13 - Doctrine version is 2.5.14 - Symfony version is 2.8.42
Entity examples :
Device : device_type
/**
* #var string
*
* #ORM\Column(name="device_type", type="string", length=500, options={ "default":"ios" })
*/
private $device_type = "ios";
UserConnection : date
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime", nullable=true)
*/
private $date;
Artisan : Creator
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="MiddlewareBundle\Entity\User")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=true)
* })
*/
private $creator;

There has been a pull request some years ago, have a look at https://github.com/doctrine/dbal/pull/2825 - it clearly states your problem:
Doctrine currently does not support MariaDB 10.2.7+. Schema creation/update is triggering infinite schema diffs
The code from this pull request has been included since doctrine/dbal:2.7.0, so you should consider updating at least that single package

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;

Doctrine default value vs null insert

There is something that bothers me. I've tried to find one clear answer but no luck so far.
I'm using Symfony3 and Doctrine2 and MariaDB.
Let's assume that I've created something like this in my entity:
/**
* #ORM\Column(
* name="status",
* type="boolean",
* options={"default": 0}
* )
*/
private $status;
Now thanks to this I have field with default value of 0 in database:
`status` tinyint(1) NOT NULL DEFAULT '0',
But what's the point of having this when every time I try to save data into database(I'm trying to save only for example 1 out of 10 fields):
$story->setContent('Test Content');
$em = $this->getDoctrine()->getManager();
$em->persist($story);
$em->flush();
I get:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'status' cannot be null
Because, of course rest of the fields on the object are null.
I can work around this by setting the default values in the constructor or by allowing for null values in the DB.
What if I don't want to do this? Is there any other way that I am missing here?
So what I would like to know:
are setting default value in entity or allowing nulls in DB only ways to do this?
is there something wrong with my logic here?
is there any cooler and cleaner way of doing this?
Like #Cerad commented, you just need to initialize the property in your actual entity class
/**
* #ORM\Column(
* name="status",
* type="boolean",
* options={"default": 0}
* )
*/
private $status = 0;

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

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;
}

Order by within group by in Doctrine 2

I'm using Symfony 2 PR12 with Doctrine 2 and MySQL. I have a database storing articles and views of those articles:
// ...
class Article {
/**
* #orm:Column(type="bigint")
* #orm:Id
* #orm:GeneratedValue
* #var int
*/
protected $id;
/**
* #orm:OneToMany(targetEntity="ArticleView",mappedBy="article")
* #var ArrayCollection
*/
protected $views;
// ...
}
// ...
class ArticleView {
/**
* #orm:Column(type="bigint")
* #orm:Id
* #orm:GeneratedValue
* #var int
*/
protected $id;
/**
* #orm:Column(type="bigint",name="DateRead",nullable=true)
* #var int
*/
protected $viewDate;
/**
* #orm:ManyToOne(targetEntity="Article",inversedBy="views")
* #var Article
*/
protected $article;
// ...
}
I want to get, for example, the 20 most-recently-viewed articles. My first thought would be something like:
$qb = <instance of Doctrine\ORM\QueryBuilder>;
$qb->select('a')
->from('Article', 'a')
->join('a.views', 'v')
->orderBy('v.viewDate', 'DESC')
->groupBy('a.id')
->setMaxResults(20)
;
However, when there's more than one view associated with an article, the order-by/group-by combination gives unpredictable results for the ordering.
This is expected behavior for MySQL, since grouping is handled before ordering, and there are working raw-query solutions to this problem at http://www.artfulsoftware.com/infotree/mysqlquerytree.php (Aggregates -> Within-group aggregates). But I can't figure out how to translate any of these solutions into DQL, since as far as I can tell there's no way to select from subqueries or perform self-exclusion joins.
Any ideas on how to solve the problem with reasonable performance?
I ended up solving it with a correlated subquery:
$qb
->select('a')
->from('Article', 'a')
->join('a.views', 'v')
->orderBy('v.viewDate', 'DESC')
->setMaxResults(20)
// Only select the most recent article view for each individual article
->where('v.viewDate = (SELECT MAX(v2.viewDate) FROM ArticleView v2 WHERE v2.article = a)')
That way the sort ignores ArticleView's other than the most recent for any given article. Though my guess is that this performs fairly poorly relative to the other raw SQL solutions - any answers with better performance would still be greatly appreciated :).