Symfony2 query error (while the same works under Doctrine2) - php

I am using both Doctrine2 and Symfony2 but Symfony2 fails me at a task I am able to accomplish in Doctrine2 (the 'standalone' version).
In Doctrine2:
I have 2 classes, Outcome, Ticket as such:
namespace Entity;
/** #Entity #Table(name="tickets") */
class Ticket {
/** #Id #Column(type="integer") #GeneratedValue(strategy="AUTO") */
private $id;
/** #Column(type="string", length=100) */
private $title;
/** #OneToMany(targetEntity="Outcome", mappedBy="ticket") */
private $outcomes;
/* with setters and getters for title and outcomes */
public function __toString() {
return $this->getTitle().' ('.$this->getId().')';
}
public function __construct () {
$this->outcomes=new \Doctrine\Common\Collections\ArrayCollection();
}
}
and
namespace Entity;
/** #Entity #Table(name="outcomes") */
class Outcome {
/** #Id #Column(type="integer") #GeneratedValue(strategy="AUTO") */
private $id;
/** #Column(type="string", length=100) */
private $text;
/** #ManyToOne(targetEntity="Ticket", inversedBy="outcomes") #JoinColumn(nullable=false) */
private $ticket;
public function __toString() {
return $this->getText().' ' .' ('.$this->getId().')';
}
}
in my index.php I use
$query = $em->createQuery ("SELECT t,o FROM Entity\Ticket t LEFT JOIN t.outcomes o WHERE t.id=1");
$query->useResultCache(true);
$tickets_2 = $query->execute();
$ticket_2 = $tickets_2[0];
$amount=count($ticket_2->getOutcomes());
echo $ticket_2." --ticket\n";
echo $amount." --outcomes\n";
and I get the correct output
now in Symfony2 I use the following definitions for the entities:
namespace test\DemoBundle\Entity;
/** #orm:Entity #orm:Table(name="tickets") */
class Ticket {
/** #orm:Id #orm:Column(type="integer")
#orm:GeneratedValue(strategy="AUTO") */
private $id;
/** #orm:Column(type="string", length=100) */
private $title;
/** #orm:OneToMany(targetEntity="Outcome", mappedBy="response") */
private $outcomes;
...
}
namespace test\DemoBundle\Entity;
/** #orm:Entity #orm:Table(name="outcomes") */
class Outcome {
/** #orm:Id #orm:Column(type="integer") #orm:GeneratedValue(strategy="AUTO") */
private $id;
/** #orm:Column(type="string", length=100) */
private $text;
/** #orm:ManyToOne(targetEntity="Ticket", inversedBy="outcomes") #orm:JoinColumn(nullable=false) */
private $ticket;
....
}
now, I've set up a controller with a test function:
namespace test\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use test\DemoBundle\Entity\Ticket as Ticket;
use test\DemoBundle\Entity\Outcome as Outcome;
class TicketController extends Controller {
...
public function testAction() {
$em = $this->get('doctrine.orm.entity_manager');
$query = $em->createQuery ("SELECT t, o
FROM test\DemoBundle\Entity\Ticket t
LEFT JOIN t.outcomes o
WHERE t.id=1");
$query->useResultCache(true);
$tickets_2 = $query->execute();
$ticket_2 = $tickets_2[0];
$amount=count($ticket_2->getOutcomes());
$prepared_result=$ticket_2." --ticket ".$amount." --outcomes ";
$response = new Response($prepared_result);
return $response;
}
}
at this point I get an error while trying to execute the last query like so:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE t0_.id = 1' at line 1
from the webdebugger I get the following syntax from PDO:
SELECT t0_.id AS id0,
t0_.title AS title1,
o1_.id AS id2,
o1_.text AS text3,
o1_.ticket_id AS ticket_id4
FROM tickets t0_
LEFT JOIN WHERE t0_.id = 1
so I guess I am missing something after the LEFT JOIN so that's why I am gettint the error in the first place?
What am I doing wrong?/What should I do?

Related

Doctrine2, ManyToMany relation => SQLSTATE[42000]: Syntax error or access violation

I am using Doctrine2 in my project and I have defined following entities:
namespace Model;
/**
* #Entity()
* #Table(name="author")
**/
class Author {
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
**/
private $id;
/** #Column(type="string") **/
private $firstName;
/** #Column(type="string") **/
private $lastName;
/** #Column(type="string", nullable=true) **/
private $titleBefore;
/** #Column(type="string", nullable=true) **/
private $titleAfter;
/** #ManyToMany(targetEntity="Article", mappedBy="authors") **/
private $articles;
public function __construct() {
$this->articles = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function getTitleBefore() {
return $this->titleBefore;
}
public function setTitleBefore($titleBefore) {
$this->titleBefore = $titleBefore;
}
public function getTitleAfter() {
return $this->titleAfter;
}
public function setTitleAfter($titleAfter) {
$this->titleAfter = $titleAfter;
}
public function getLastName() {
return $this->lastName;
}
public function setLastName($lastName) {
$this->lastName = $lastName;
}
public function getFirstName() {
return $this->firstName;
}
public function setFirstName($firstName) {
$this->firstName = $firstName;
}
public function getArticles() {
return $this->articles;
}
public function addArticle($article) {
$this->articles->add($article);
}
}
and
namespace Model;
/**
* #Entity()
* #Table(name="article")
**/
class Article {
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
**/
private $id;
/** #Column(type="string") **/
private $name;
/** #OneToOne(targetEntity="Publication") **/
private $publication;
/**
* #ManyToMany(targetEntity="Author", mappedBy="articles")
*/
private $authors;
public function __construct() {
$this->authors = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getPublication() {
return $this->publication;
}
public function setPublication($publication) {
$this->publication = $publication;
}
public function getAuthors() {
return $this->authors;
}
public function addAuthor($author) {
$this->authors->add($author);
$author->addArticle($this);
}
public function setAuthors($authors) {
$this->authors = $authors;
}
}
It looks like, that relation author<->article works nicely. Although I encountered a problem. When I try to acces authors in Smarty template like this: {foreach from=$article->getAuthors() item=author}, following exception is thrown:
Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ON' at line 1 in /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:104 Stack trace:
#0 /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php(104): PDO->query('SELECT t0.id AS...')
#1 /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php(852): Doctrine\DBAL\Driver\PDOConnection->query('SELECT t0.id AS...')
#2 /code/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php(1030): Doctrine\DBAL\Connection->executeQuery('SELECT t0.id AS...', Array, Array)
#3 /code/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php(954): Doctrine\ORM\Persisters\Entity\BasicEntityPersister->getManyToManyStatement(Array, Object(Model\Article))
#4 /code/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(2839): Doctrine\ORM\Persiste in /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php on line 90
I already spent a day on this, trying to find out what went wrong. I was suspicious, that I might use a reserved MySQL word, but I didn't find any in my variables.
I finally managed to obtain complete query log until I got the exception. It looks like, the most interesting query is not present
mysqld, Version: 5.7.20 (MySQL Community Server (GPL)). started with:
Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock
Time Id Command Argument
2017-11-25T23:46:52.327597Z 10 Connect user#articlerepository_php_1.articlerepository_default on article_repository using TCP/IP
2017-11-25T23:46:52.334083Z 10 Query SELECT t0.id AS id_1, t0.firstName AS firstName_2, t0.lastName AS lastName_3, t0.titleBefore AS titleBefore_4, t0.titleAfter AS titleAfter_5 FROM author t0
2017-11-25T23:46:52.342077Z 10 Query SELECT t0.id AS id_1, t0.name AS name_2, t0.publication_id AS publication_id_3 FROM article t0
2017-11-25T23:46:52.348058Z 10 Quit
Looks like I had a mismatch in my ManyToMany relationship. Here are the steps I took to fix this:
I dropped current schema by running bin/doctrine orm:schema-tool:drop --force
Validated current entities with running bin/doctrine orm:validate several times and made changes in entities, until it went through without errors
Generated new schema: bin/doctrine orm:schema-tool:update --force --dump-sql
Correct relationship looks like this:
class Author {
/**
* #ManyToMany(targetEntity="Article", inversedBy="authors")
* #JoinTable(name="authors_articles")
**/
private $articles;
}
class Article {
/**
* #ManyToMany(targetEntity="Author", mappedBy="articles")
*/
private $authors;
}
`

Building relationship entity with Neo4J PHP OGM EntityManager

I am trying to build an entity object for my relationship in Neo4j database with GraphAware Neo4j PHP OGM library using this simple method:
public function getRelationshipEntity($entityId) {
$repo = $this->entityManager->getRepository( Entity\Relationship\Fired::class );
return $repo->findOneById($entityId);
}
Here we have the entity classes, relationship first:
namespace Entity\Relationship;
use GraphAware\Neo4j\OGM\Annotations as OGM;
use Entity\Issue;
use Entity\Event;
/**
* #OGM\RelationshipEntity(type="FIRED")
*/
class Fired {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #OGM\StartNode(targetEntity="Entity\Event")
*/
protected $event;
/**
* #OGM\EndNode(targetEntity="Entity\Issue")
*/
protected $issue;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $time;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $eventName;
}
Then, start node:
namespace Entity;
use GraphAware\Neo4j\OGM\Annotations as OGM;
/**
* #OGM\Node(label="Event")
*/
class Event {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $name;
}
..and end node:
namespace Entity;
use Doctrine\Common\Collections\ArrayCollection;
use GraphAware\Neo4j\OGM\Annotations as OGM;
/**
* #OGM\Node(label="Issue")
*/
class Issue {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #OGM\Property(type="string")
*/
protected $key;
/**
* #OGM\Property(type="string")
*/
protected $created;
/**
* #OGM\Property(type="string")
*/
protected $updated;
/**
* #OGM\Relationship(type="FIRED", direction="INCOMING", relationshipEntity="Entity\Relationship\Fired", collection=true)
* #var ArrayCollection
*/
protected $eventFires;
public function __construct($key) {
$this->key = $key;
$this->eventFires = new ArrayCollection();
}
public function __wakeup() {
$this->__construct($this->key);
}
/**
* #return ArrayCollection
*/
public function getEventFires() {
return $this->eventFires;
}
public function addEventFire(Entity\Relationship\Fired $eventFired) {
$this->eventFires->add($eventFired);
}
public function removeEventFire(Entity\Relationship\Fired $eventFired) {
$this->eventFires->removeElement($eventFired);
}
}
Apparently, what works really well for node entites, triggers the following error for relationships:
Fatal error: Call to undefined method GraphAware\Neo4j\OGM\Metadata\RelationshipEntityMetadata::hasCustomRepository() in /vendor/graphaware/neo4j-php-ogm/src/EntityManager.php
Any suggestion how I could workaround this? I even tried using EntityManager::createQuery() the following way:
public function getRelationships($eventName) {
$query = $this->entityManager->createQuery('MATCH (e:Event)-[f:FIRED{eventName: {eventName}}]->() RETURN e,f ORDER BY f.time');
$query->addEntityMapping('e', 'Entity\Event' );
$query->addEntityMapping('f', 'Entity\Relationship\Fired' );
$query->setParameter( 'eventName', $eventName);
return $query->execute();
}
But, apparently, addEntityMapping() doesn't work for relationship entities either! (It might be a feature though, not a bug):
Catchable fatal error: Argument 1 passed to GraphAware\Neo4j\OGM\Hydrator\EntityHydrator::hydrateNode() must implement interface GraphAware\Common\Type\Node, instance of GraphAware\Bolt\Result\Type\Relationship given, called in /vendor/graphaware/neo4j-php-ogm/src/Query.php on line 145 and defined in /vendor/graphaware/neo4j-php-ogm/src/Hydrator/EntityHydrator.php on line 232
So, I ended up that I can easily define and store relationship entities in Neo4J with this library but not sure how I could retrieve it easily with EntityManager, in the similar way I can do so with nodes.
Any help would be much appreciated!
As requested in comment below, these are GraphAware packages that I am using:
graphaware/neo4j-bolt 1.9.1 Neo4j Bolt Binary Protocol PHP Driver
graphaware/neo4j-common 3.4.0 Common Utilities library for Neo4j
graphaware/neo4j-php-client 4.8.0 Neo4j-PHP-Client is the most advanced PHP Client for Neo4j
graphaware/neo4j-php-ogm 1.0.0-RC6 PHP Object Graph Mapper for Neo4j

Variable undefined when using Doctrine QueryBuilder

I want to make alternative way for making stored procedures by using Doctrine but I am stuck, could any one help me?
Example stored procedure to be formed:
CREATE PROCEDURE catalog_get_department_details(IN DepartmentName)
BEGIN
SELECT name, description
FROM
department
WHERE name = name;
Departments Entity:
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\departmentsRepository")
* #ORM\Table(name="departments")
*/
class departments
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $department_id;
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Column(type="string", nullable=true)
*/
private $description;
/**
* #ORM\OneToMany(targetEntity="categories",mappedBy="departments")
*/
private $categories;
function __construct()
{
$this->categories = new ArrayCollection();
}
public function getDepartmentId()
{
return $this->department_id;
}
public function setDepartmentId($department_id)
{
$this->department_id = $department_id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getDescription()
{
return $this->description;
}
/**
* #param mixed $description
*/
public function setDescription($description)
{
$this->description = $description;
}
The scenario is when the route is /index/departmentname/Regional ;
my DefaultController will capture Regional as parameter
DefaultController:
class DefaultController extends Controller
{
/**
* #Route ("/index/department/{department_name}")
*/
function departmentAction($department_name)
{
// accessing departmentsRepository
$categoriesRepository = $this->getDoctrine()->getManager()
->getRepository('AppBundle:departments');
$categoriesRepository->getDepartmentDetails($department_name);
}
departmentsRepository:
class departmentsRepository extends \Doctrine\ORM\EntityRepository
{
function getDepartmentDetails($departmentName)
{
$em=$this->getEntityManager()->getRepository('AppBundle:departments');
$qb=$em->createQueryBuilder('dep');
$qb->select('dep.name','dep.description');
$qb->where("dep.name=$departmentName");
When I call var_dump($qb->getDQL());die; it shows me exactly what I want:
SELECT dep.name, dep.description FROM AppBundle\Entity\departments dep WHERE dep.name=Regional
I then execute it by calling
$qb->getQuery()->execute();
But I receive the following error:
[Semantical Error] line 0, col 86 near 'Regional': Error: 'Regional'
is not defined.
Any idea what I'm doing wrong?
Your dep.name value isn't being escaped. You would expect the query to look like this instead:
WHERE dep.name='Regional'
But what you should be doing, and what is safer, is binding that to a parameter, like so:
$em = $this->getEntityManager()->getRepository('AppBundle:departments');
$qb = $em->createQueryBuilder('dep');
$qb->select('dep.name', 'dep.description');
$qb->where("dep.name = :departmentName");
$qb->setParameter('departmentName', $departmentName);
Doctrine will handle the escaping for you, and safely. This also allows you to avoid SQL injection attacks. Also since you are already in your departments repository you should be able to use the _em value as a shortcut, and also not have to re-specify the departments entity, like so:
$qb = $this->_em->createQueryBuilder('dep');
$qb->select('dep.name', 'dep.description');
$qb->where("dep.name = :departmentName");
$qb->setParameter('departmentName', $departmentName);
Side not, in your controller action you are calling the repository function but not actually saving the results to any variable.

Slow Doctrine Find

I am trying to figure out why one of my doctrine finds is running so slow. I don't really know where to start, so please bear with me.
I do a pretty basic find to fetch a user object. This find is taking ~160ms. When I run the query via phpmyadmin, it takes .7ms.
$this->em->find('Entities\User', $userId)
I have already tried adding skip-name-resolve to mysql's my.cnf. The id field in the user table is indexed. I really don't know what else to try. Let me know if there is additional information I can provide.
Below is the entity file:
namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityRepository;
/** #Entity(repositoryClass = "Entities\UserRepository")
* #Table(name="user")
*/
class User extends \Company_Resource_AbstractEntity
{
/** #Id #Column(type="integer") #GeneratedValue */
protected $id;
/** #Column(type="string") */
protected $name;
/** #Column(type="string") */
protected $password;
/** #Column(type="string") */
protected $email;
/** #Column(type="string") */
protected $first_name;
/** #Column(type="string") */
protected $last_name;
/** #Column(type="integer") */
protected $password_reset;
/** #Column(type="string") */
protected $salt;
/** #Column(type="integer") */
protected $active;
/** #Column(type="string") */
protected $cookie_hash;
/**
* #ManyToOne(targetEntity="Company" , inversedBy="user")
*/
protected $company;
/**
* #ManyToOne(targetEntity="Privilege" , inversedBy="user")
*/
protected $privilege;
/**
* #OneToMany(targetEntity="CompanySubscription" , mappedBy="user")
*/
protected $subscription;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_in_user")
*/
protected $check_in;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_out_user")
*/
protected $check_out;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_in_user")
*/
protected $check_in_group;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_out_user")
*/
protected $check_out_group;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="submit_user")
*/
protected $maintenance_submit;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="completed_user")
*/
protected $maintenance_complete;
/**
* #OneToMany(targetEntity="UserLogin" , mappedBy="user")
*/
protected $login;
}
Abstract entity:
use \Doctrine\Common\Collections\ArrayCollection;
abstract class Company_Resource_AbstractEntity implements ArrayAccess
{
public function offsetExists($offset)
{
return property_exists($this, $offset);
}
// The get/set functions should check to see if an appropriately named function exists before just returning the
// property. This way classes can control how data is returned from the object more completely.
public function offsetGet($offset)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'get'. $property->filter($offset);
return $this->{$method}();
}
public function offsetSet($offset, $value)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'set'. $property->filter($offset);
return $this->{$method}($value);
}
public function offsetUnset($offset)
{
// can't do this
}
/*==-====-====-====-====-====-====-====-====-====-====-==*/
/*
* Provides magic method access for getFieldName() and setFieldName()
* where field_name is a simple field and not a relation
* A special getData implementation returns all of the current object vars
*/
public function __call($method, $arguments)
{
preg_match('#^([a-z]+)(.*)#', $method, $matches);
$action = $matches[1];
$property = $matches[2];
$underscore = new Zend_Filter_Word_CamelCaseToUnderscore();
$offset = strtolower($underscore->filter($property));
if ($action == 'get')
{
if ($property == 'Data')
return get_object_vars($this);
if ($this->offsetExists($offset))
return $this->{$offset};
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else if ($action == 'set')
{
if ($this->offsetExists($offset))
return $this->{$offset} = $arguments[0];
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else
throw new Zend_Exception(sprintf("'%s' does not have method '%s'", get_class($this), $method));
}
}
The SQL that the find produces:
SELECT t0.id AS id1,
t0.name AS name2,
t0.password AS password3,
t0.email AS email4,
t0.first_name AS first_name5,
t0.last_name AS last_name6,
t0.password_reset AS password_reset7,
t0.salt AS salt8,
t0.active AS active9,
t0.cookie_hash AS cookie_hash10,
t0.company_id AS company_id11,
t0.privilege_id AS privilege_id12
FROM user t0 WHERE t0.id = ?
Anyone see anything wrong or know where to go further with this?
Using Doctrine 2.2.2.
The explain I get when I run that query with phpmyadmin: http://i.imgur.com/wWeGO.png
The table schema: http://i.imgur.com/BQsRX.jpg
I believe the problem with my setup was the actual number of lines in the file. Doctrine was reading through those every time. I enabled APC for the meta-cache and load time decreased dramatically after the first load. Without query or result cache, that query ACTUALLY only takes about 6 MS which is what I was aiming for all along. Wish I would have tried that sooner.

Doctrine 2 PlainValue expected

I'm having trouble executing a Doctrine DQL Query. This is the error it gives me.
Doctrine\Common\Annotations\AnnotationException: [Syntax Error] Expected PlainValue,
got 'integer' at position 13 in property Base\Session::$lifetime.
My code looks like this:
$query = $em->createQuery("SELECT s FROM Base\Session s WHERE s.session = \"$id\"");
Where $id is the current session_id. My Model looks like:
namespace Base;
/** #Entity #Table(name="session") */
class Session extends Skeleton {
/**
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #Column(length=32) */
protected $session;
/** #Column(type=integer) */
protected $lifetime;
/** #Column(type=integer) */
protected $modified;
/** #Column(type="text") */
protected $data;
}
You have two errors in this:
You have to double quote your annotations, i.e. #Column(type="integer") not #Column(type=integer). Doctrine\Common\Annotations\AnnotationException is thrown when your mapping is wrong. This has nothing to do with the query.
Your query should use prepared statements, i.e.
$query = $em->createQuery("SELECT s FROM Base\Session s WHERE s.session = ?1");
$query->setParameter(1, $id);

Categories