Doctrine findBy boolean field returns no results - mysql

Recently, a piece of code stopped working. I haven't made any changes to it so I don't know why.
Here's the code:
$invites = $this->vault_em->getRepository('AppBundle:Invite\LocalInvite')->findBy([
'active' => true,
]);
Now, it's returning an empty array, even though there are LocalInvite records with active = 1.
Here are the doctrine mappings:
/**
* #ORM\Entity
* #ORM\Table(name="invite")
*/
class LocalInvite extends Invite {
//...
}
/** #ORM\MappedSuperclass */
abstract class Invite implements \JsonSerializable {
/** #ORM\Column(type="boolean", options={"default": true}) */
protected $active;
//...
}
To debug, I copied the underlying MySQL query that Doctrine is executing from the debug logs:
SELECT t0.id AS id_1, t0.email AS email_2, t0.active AS active_3, t0.location AS location_4, t0.cohort_leadership AS cohort_leadership_5, t0.timezone AS timezone_6, t0.date_record_created AS date_record_created_7, t0.date_record_deleted AS date_record_deleted_8, t0.date_restart AS date_restart_9, t0.date_start_invite AS date_start_invite_10, t0.employee_id AS employee_id_11, t0.first_name AS first_name_12, t0.corporate_client_name AS corporate_client_name_13, t0.client_id AS client_id_14, t0.landing_page_url AS landing_page_url_15, t0.user_id AS user_id_16, t0.recipient_id AS recipient_id_17 FROM invite t0 WHERE t0.active = true;
When I plug that query into a MySQL IDE, it returns results.
Why does the findBy return no results?

try to change 'AppBundle:Invite\LocalInvite' by LocalInvite::class

Related

Doctrine: addSelect method creates undesired seperate object (Symfony)

I want to query the entity based on a calculated property that does not exist in the database or on the entity.
If I run
return $this->createQueryBuilder('b')
->select('b')
->addSelect(
'... as extra_property'
)
->having('extra_property = :param')
->setParameter('param', $param)
->orderBy('extra_property', 'ASC')
->getQuery()
->getResult();
This results in a collection with each entity in the following format:
"0": {}, // The 9 entity properties
"extra_property": "value"
However, I want the extra_property to be added to the other entity properties as the tenth property. How do I fix this?
The problem lies in the getResult() method, the default hydration method is "HYDRATE_OBJECT" which will try to hydrate the output as the defined entity, which does not know about your extra property.
public function getResult($hydrationMode = self::HYDRATE_OBJECT)
{
return $this->execute(null, $hydrationMode);
}
Depending on what your other properties are, you could just use the "HYDRATE_SCALAR" option which will just give you a flat output of the results.
If you other properties are nested entities, you will have to manually select the fields in your select to bypass the hydration process, or find a way to add that property to your entity and tell the hydrator to get that data from a dedicated query or something.
You can find the different hydration methods as constants of the AbstractQuery class, you can read more on the Doctrine documentation
/**
* Hydrates an object graph. This is the default behavior.
*/
public const HYDRATE_OBJECT = 1;
/**
* Hydrates an array graph.
*/
public const HYDRATE_ARRAY = 2;
/**
* Hydrates a flat, rectangular result set with scalar values.
*/
public const HYDRATE_SCALAR = 3;
/**
* Hydrates a single scalar value.
*/
public const HYDRATE_SINGLE_SCALAR = 4;
/**
* Very simple object hydrator (optimized for performance).
*/
public const HYDRATE_SIMPLEOBJECT = 5;
/**
* Hydrates scalar column value.
*/
public const HYDRATE_SCALAR_COLUMN = 6;

symfony doctrine will not default a boolean to 0

I have tried in several ways to have symfony default a boolean to 0 rather than null (as null gives me a database level error upon flush).
An exception occurred while executing a query: SQLSTATE[23000]:
Integrity constraint violation: 1048 Column 'auto_created' cannot be
null
This made no difference:
/**
* #ORM\Column(type="boolean", options={"default":"0"})
*/
private $autoCreated;
Some logic i the setter made no difference either
public function setAutoCreated(bool $autoCreated): self
{
if is_null($autoCreated) {
$autoCreated = 0;
}
$this->autoCreated = $autoCreated;
return $this;
}
As well as
public function setAutoCreated(bool $autoCreated): self
{
if is_null($autoCreated) {
$autoCreated = false;
}
$this->autoCreated = $autoCreated;
return $this;
}
Database looks like this
I am clearly missing something...?
Sure I can do a simple $user->setAutoCreated(false); everywhere I create this entity, but I don't get why I should have to 😎
Depending on the version of PHP you're using, you should be able to do something like this in your entity class:
/**
* #ORM\Column(type="boolean")
*/
private $autoCreated = false;
Whenever an instance of this class is created, $autoCreated will be set to false. So when you try to persist the object, it'll have a default value of false and Doctrine will set the field to 0.
Alternatively, you can explicitly set $autoCreated to false in your constructor:
public function __construct()
{
$this->autoCreated = false;
}
Note that you can only use the first approach for simple, built-in PHP types or constants. For more complicated objects (e.g., a Doctrine ArrayCollection) you'll need to use the constructor approach.

How to do a join on two entities in Symfony and Doctrine?

I have a select query and I'm trying to add a join to it.
In the example below, I have a Questionentity that I use to return some results, and I want to add a join with the User entity, like:
SELECT question FROM question AS q LEFT JOIN USER u ON q.user_id= u.id;
I would like the result to be a User entity inside a Question entity, something like:
private Question (entity)
private id
private user_id
private User (entity)
private id
private name
here is my class
namespace AppBundle\Repository;
use AppBundle\Entity\User;
use AppBundle\Entity\Question;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;
class QuestionRepository extends EntityRepository
{
/**
* #param int $currentPage
*
* #return Paginator
*/
public function getQuestions($currentPage = 1)
{
$questions = $this->createQueryBuilder('question')
->where('question.active is NULL')
->getQuery();
$paginator = $this->paginate($questions, $currentPage);
return $paginator;
}
}
I call it like this
$questionRepo = $this->container->get('doctrine')->getManager()->getRepository('AppBundle:Question');
$questions = $questionRepo->getQuestions(1);
Any ideas?
How about that:
$questions = $this->createQueryBuilder('question')
->leftJoin('question.user', 'question_user', 'WITH', 'question_user.user = :user_id')
->where('question.active is NULL')
->setParameter('user_id', $user_id)
->getQuery();
$paginator = $this->paginate($questions, $currentPage);
Edit: Because of your latest comments, i have to mention, that this suggestion is assuming that your Question entity looks like this (according User's entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="question")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
If not, add this, generate entities (php app/console doctrine:generate:entities AppBundle:Question) and update DB (php app/console doctrine:schema:update --force).
PS: Before generating entities, you'll have to remove old getters/setters.

QueryDSL and date diff

I'm trying to compute user age with a date difference using QueryDSL.
QPlayer $ = QPlayer.player;
BooleanBuilder builder = new BooleanBuilder();
builder.and(Expressions.dateOperation(Integer.class, Ops.DateTimeOps.DIFF_YEARS, Expressions.currentDate(), $.birthDate).between(3, 5));
playerRespository.findAll(builder);
But fail with this error
Hibernate: select player0_.user_id as id1_31_, player0_1_.user_birthdate as user_bir2_31_, player0_1_.user_register_date as user_reg3_31_, player0_1_.user_delete_date as user_del4_31_, player0_1_.user_email as user_ema5_31_, player0_1_.user_first_name as user_fir6_31_, player0_1_.user_last_name as user_las7_31_, player0_1_.user_login as user_log8_31_, player0_1_.user_password as user_pas9_31_, player0_1_.user_status as user_st10_31_, player0_.player_description as player_d1_20_, player0_.player_height as player_h2_20_, player0_.player_picture as player_p3_20_, player0_.player_role as player_r4_20_, player0_.player_weight as player_w5_20_ from players player0_ inner join users player0_1_ on player0_.user_id=player0_1_.id where (diff_years(player0_1_.user_birthdate, current_date) between ? and ?) and (lower(player0_1_.user_first_name) like ? escape '!')
2015-07-19 14:22:16,881 [main] ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - FUNCTION xxx.diff_years does not exist
This errors occurs using both MYSQL database or HSQL.
What's wrong with that code?
Thanks
diff_years is unfortunately not yet supported in Querydsl JPA
How to customize MYSQL dialect with queryDsl. I show how to use datediff for select statement.
Custom query request (calculate difference between now and createdDate):
public List<MyDto> get() {
JPAQuery<HotelShortDto> query = new JPAQuery<>(em, MySQLJPQLTemplates.DEFAULT)
.select(Projections.constructor(MyDto.class,
Expressions.dateOperation(Integer.class, Ops.DateTimeOps.DIFF_MINUTES, Expressions.currentDate(), myEntity.createdDate),
))
.from(myEntity)
return query.fetch();
}
In MYSQL function to get difference in minutes is TIMESTAMPDIFF (for H2 db datediff)
Configuration:
public class MySQLJPQLTemplates extends JPQLTemplates {
public static final MySQLJPQLTemplates DEFAULT = new MySQLJPQLTemplates();
public MySQLJPQLTemplates() {
this(DEFAULT_ESCAPE);
add(Ops.DateTimeOps.DIFF_MINUTES, "TIMESTAMPDIFF(MINUTE,{0},{1})");
}
public void reconfigureForH2() {
add(Ops.DateTimeOps.DIFF_MINUTES, "datediff(MINUTE,{0},{1})");
}
public MySQLJPQLTemplates(char escape) {
super(escape);
}
}
Also TIMESTAMPDIFF is not standart function for hibernate, so registration is needed
public class CustomMySQLDialect extends MySQL57Dialect {
public CustomMySQLDialect() {
super();
registerFunction("TIMESTAMPDIFF", new StandardSQLFunction("TIMESTAMPDIFF"));
}
}
and application.yaml
...
spring.jpa.database-platform=com.my.project.CustomMySQLDialect
...
For testing call before execute query
MySQLJPQLTemplates.DEFAULT.reconfigureForH2();

Doctrine2 multiple join

I have problem with my User Entity. I have code generated by Doctrine it is below:
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Frontend\UserBundle\Entity\SfGuardPermission", inversedBy="user")
* #ORM\JoinTable(name="sf_guard_user_permission",
* joinColumns={
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="permission_id", referencedColumnName="id")
* }
* )
*/
protected $permission;
Problem is with join because I can't join user and permission. What I must to do? I must join sf_guard_user with sf_guard_user_group with sf_guard_grop with sf_guard_group_permission with sf_guard_permission. Because I need to get User permission. I do not no how to write join like this in code above. Is it possible?
You can not write this join in one annotation. In fact you gone have three entity tables sf_guard_user, sf_guard_group and sf_guard_permission and two cross tables which you can write as you already started, sf_guard_user_group and sf_guard_group_permission.
But since it looks like you try to migrate some symfony 1.x stuff to symfony 2.x:
The sf_guard_user_permisson table in symfony 1.x is a cross table between users and permission, containing extraordinaire permission for a user which are not granted through the groups the user is in, so you are already done.
SBH thx for replay, of course you have right with everything what you have written. But my sf_guard_user_permisson is empty so I can't use it. I can generate this table, this is no problem, but then I will must maintain it. This is next work for me so i wrote code below:
namespace Frontend\UserBundle\Entity;
// ...
/**
* #var \Doctrine\Common\Collections\Collection
*
*/
protected $permissions;
/**
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPermissions()
{
$groups = $this->getSfGuardGroups();
foreach ($groups as $group)
{
$groupPermisions = $group->getPermission();
foreach ($groupPermisions as $groupPermision)
{
if (!in_array($groupPermision, $this->permissions)) {
$this->permissions[] = $groupPermision;
}
}
}
return $this->permissions;
}
/**
* #param string $permissionName
* #return boolean
*/
public function hasPermission($permissionName)
{
$this->getPermissions();
foreach ($this->permissions as $permission)
{
if($permission->getName() === $permissionName) {
return true;
}
}
return false;
}
// ..
What do you think about it? Your opinion is very important for me.
Edit:
Thx for SBH help, I have got answer for my question. I have hope it will help other people. If you do not understand something please look at SBH answer.