Missing insert() method on Doctrine DBAL Query Builder - php

I feel like I'm having a moment where I'm missing something small here; I've been having issues using the insert() method on the QueryBuilder component on Dotrine DBAL 2.2.x / 2.3.x.
I did some investigation and here's the snippet from the QueryBuilder page from the DBAL Documantation
The \Doctrine\DBAL\Query\QueryBuilder supports building SELECT, INSERT, UPDATE and DELETE queries. Which sort of query you are building depends on the methods you are using.
It goes on further to explain code examples, such that I can simply do:
$builder = $connection->createQueryBuilder();
$result = $builder
->insert('table_name')
// ...
To use the query builder in Insert Mode. Except when I do I get a complaint here from PHP:
Fatal error: Call to undefined method Doctrine\DBAL\Query\QueryBuilder::insert()
On further inspection of The QueryBuilder.php Source Code
I see no reference to any method insert(...), no class to inherit this from, no traits added to the QueryBuilder that could expose the insert mechanism. In addition I see this right at the top:
/* The query types. */
const SELECT = 0;
const DELETE = 1;
const UPDATE = 2;
There's no insert query type; there is however this interesting method comment for execute():
/**
* Execute this query using the bound parameters and their types.
*
* Uses {#see Connection::executeQuery} for select statements and {#see Connection::executeUpdate}
* for insert, update and delete statements.
*
* #return mixed
*/
Bottom Line:
This is a massive project with 100's of maintainers, I'm more likely to find my interpretation suspect here than a screwup on something so fundamental over numerous versions, but I cannot for the life of me figure out what I'm missing. Please help me see the obvious.

It depends on your version. Insert has been added since v2.5.0-BETA3.
Viz https://github.com/doctrine/dbal/blob/master/lib/Doctrine/DBAL/Query/QueryBuilder.php#L563
and commit
You can decide to update package version or check this alternative solution

Related

Eloquent: where are queries executed

Atm I am building a very specific solution for an existing application written in Laravel. The solution executes queries in c++ modifies data, does sorting and returns the results. This c++ program is loaded in via a PHP extension and serves a single method to handle this logic.
The method provided by the extension should be implemented in Laravel using Eloquent, I've been looking at the source code for ages to find the specific method(s) that execute the queries build with Eloquensts Builder.
Where can I find the methods that actually perform the queries?
Why c++? I hear you think. The queries should be executed on multiple schemas (and/or databases) over multiple threads for improved performance. Atm 100+ schemas are being used with each containing thousands of records per table.
After a lot of troubleshooting and testing I have found a solution to my problem. In the class Illuminate\Database\Query\Builder you can find a method called runSelect(). This method runs a select statement against the given connection and returns the selected rows as an array.
/**
* Run the query as a "select" statement against the connection.
*
* #return array
*/
protected function runSelect()
{
return $this->connection->select(
$this->toSql(), $this->getBindings(), ! $this->useWritePdo
);
}
What I did to test my implementation in c++ to run the selects, I mapped the return values of $this->getBindings() to a new array to do some necessary modifications to some strings and did a simple str_replace_array on the prepared statement to get the full query. Eventually the c++ program will execute the prepared statemend and not the parsed query.
The modified method to suit my case looks like this. This has been done quick and dirty for now to test if it is possible, but you get the idea. Works as a charm except for the count() method in eloquent.
/**
* Run the query as a "select" statement against the connection.
*
* #return array
*/
protected function runSelect()
{
$bindings = [];
foreach ($this->getBindings() as $key => $value) {
$bindings[] = $value; // Some other logic to manipulate strings will be added.
}
$query = str_replace_array('?', $bindings, $this->toSql());
$schemas = ['schema1', 'schema2', 'schema3', 'schema4']; // Will be fetched from master DB.
return runSelectCpp($schemas, $query);
}

How to test custom DQL function?

I wrote a custom DQL function based on this tutorial.
I would like to write some tests, but the functionality of DQL is quite complex.
Is there any way how to test it?
As I see, even doctrine project not tests own DQL functions :(
I know it's an old quesion but for the folks who goggled it out as I did.
Testing is simple in fact. The general idea is to generate a query using your custom funciton then compare it against the expected result. Here is the example.
Let's say you have a custom DQL function which translates MY_FUNC(field) to MySQL_INTERNAL_FUNC(field). Then you test it as the following.
/* Somewhere in the PHPUnit test */
/* Assuming $em represents your EntityManager instance */
$query = $em->createQuery('
SELECT MY_FUNC(entity.someField)
FROM MyBundle:MyEntity entity
');
self::assertEquals('
SELECT MySQL_INTERNAL_FUNC(v0_.some_field) AS sclr_0
FROM my_entity v0_
', $query->getSQL());
That does the trick. Note the indents. They are there just to make the code clear. In practice Doctrine will remove them making SQL. So the expected SQL code should be in one line.

Doctrine: How to order by attribute from association with Criteria

I am not able to order records by attribute from another entity.
I have class Employee, which has attribute $user, which points to class User, which have attribute $active.
Now I want to sort Employee by user.active and I am not able to do that. This is how I call the em:
/**
* #param Criteria $criteria
* #param array $order_by
* #param integer $limit
* #param integer $offset
* #return \Doctrine\Common\Collections\Collection
*/
public function findByCriteria($criteria, $order_by = null, $limit = null, $offset = null) {
$criteria->setFirstResult($offset);
$criteria->setMaxResults($limit);
$criteria->orderBy($order_by);
$result = $this->repository->matching($criteria);
return $result;
}
I inspected BaseEntityPersister.php and it seems like there is no implementation of such a thing. It just checks if user.active is an attribute of Employee class and throws
Doctrine\ORM\ORMException
Unrecognized field: user.active
I know I can do that via QueryBuilder and joins, but I want to make my code more reusable and Criteria seemed like a good choice.
Thank you for your advices!
EDIT:
If I use findBy, there is no problem with sorting field user.active. Should I consider this a limitation of matching method? It is sad, because I need to use Doctrine\Common\Collections\Criteria. I can use findBy with order and then use matching method to filter records, but I would rather do that on the database side.
EDIT 2:
I use Nette with Kdyby/Doctrine. Didn't know that user.active is implemented in Kdyby/doctrine and not in Doctrine directly. So I suppose this question won't be answered..
If you look at Kdyby/Doctrine, is extends the findBy capabilities by automatically detecting relations and performing join if needed as cane seen here by calling autoJoinOrderBy.
This is the reason why criteria does not support join, and Kdyby's findBy does.
If you want reusable way to build Doctrine queries, the Criteria does not help you with that, but there is QueryObject, which does the similar job and lets you reuse logical query parts, but using QueryBuilder.
It is not so well documented, but here are some resources:
Official documentation of QueryObject
Blog post with example usage
Kdyby/Doctrine autor's presentation on Doctrine and QueryObject (from slide 43)
Some more official information about QueryObjects

Symfony2 - Show all tables from database

I would like to retrieve all the tables of my database as a list.
i tried to do a "Show databases" on a query but as i'm not using a class I defined (entity) in symfony it's not working.
And with DQL :
$em = $this->getDoctrine()->getEntityManager();
$query = $em->createQuery(
'show databases');
$result = $query->getResult();
This error :
[Syntax Error] line 0, col 0: Error: Expected SELECT, UPDATE or DELETE, got 'show'
Any idea to help me ?
As mentioned in a different answer, you can use Doctrine\DBAL for that:
/** #type \Doctrine\DBAL\Connection $connection */
$connection = ...;
/** #type \Doctrine\DBAL\Schema\MySqlSchemaManager $sm */
$sm = $connection->getSchemaManager();
And then just list the tables as Array:
var_dump( $sm->listDatabases() );
My 2 cents:
getContainer()->get('doctrine.dbal.default_connection')->getSchemaManager()->listTableNames()
This will give you an array of table names.
i have a couple cases where I need to use complex sql statements/functions that I just couldn't do in DQL. Luckily, Symfony2/doctrine provide a method to grab the current database connection and bypass doctrine entirely.
//get connection
$conn = $this->get('database_connection');
//run a query
$users= $conn->fetchAll('select * from users');
Be very careful when using this method, however. Since you are bypassing doctrine, you need to handle any security concerns like SQL injection yourself.
You can add Doctrine DBAL to your project and it will give you the tools you need. You can list databases, tables from a database, columns from a table etc etc
More in Doctrine DBAL documentation: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/schema-manager.html
Doctrine is a ORM, it is not intended to list all the databases. Beside that, usually for the current user you don't have the right to show all databases in the server, this can prove to be a big security breach.
Basicly, doctrine does not know how to interpret your query, you have to use a native query for this: Doctrine Native Query

How to retrieve an entity with all of its associations using EntityManager in Doctrine2?

I have a simple entity with many-to-many and one-to-many associations. I'm aware of 'Joins' for fetching related associations which is a manual solution for my problem.
How can I fetch an entity with all of its associations using EntityManager in Doctrine2? e.g.:
$this->em
->getRepository('Entities\Patientprofile')
->findOneByuserid('555555557')
->fetchAllAssociations();
from http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql
you can set eager fetch mode temporarily:
$query = $em->createQuery("SELECT u FROM MyProject\User u");
$query->setFetchMode("MyProject\User", "address", "EAGER");
$query->execute();
If you want do load dynamically all associations with this fetch mode, you can use the getAssociationMappings() method of the Doctrine\ORM\Mapping\ClassMetadataInfo, passing your entity name as parameter to the constructor of ClassMetadataInfo and then iterate over the returned array as $assoc and call:
$query->setFetchMode("MyProject\User", $assoc, "EAGER");
Doc: ClassMetadataInfo#getAssociationMappings()
Doctrine2 setFetchMode not working with "EAGER"
I tried also to fetch the associating entities "eagerly" using setFetchMode in my query, but the following didn't seem to work:
$query->setFetchMode("MyProject\User", "address", "EAGER");
When I jumped into the files I found out that the third parameter $fetchMode should be an integer. The constants are defined in Doctrine\ORM\Mapping:ClassMetadataInfo. When passing a string it will default to Mapping\ClassMetadata::FETCH_LAZY because of this if clause.
/**
* Specifies that an association is to be fetched when it is first accessed.
*/
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
*/
const FETCH_EAGER = 3;
/**
* Specifies that an association is to be fetched lazy (on first access) and that
* commands such as Collection#count, Collection#slice are issued directly against
* the database if the collection is not yet initialized.
*/
const FETCH_EXTRA_LAZY = 4;
So setting the corresponding integer solved the problem:
$query->setFetchMode("MyProject\User", "address", 3);
Or declare the class use Doctrine\ORM\Mapping\ClassMetadata at the top and then use the constant:
$query->setFetchMode("MyProject\User", "address", ClassMetadata::FETCH_EAGER);
EDIT:
Since there seems to be a lot of confusion here on how to fetch associations the right way I will edit my answer and add some additional information on how you can fetch join using your repository.
According to the Doctrine documentation there are 2 types of joins:
Regular Joins: Used to limit the results and/or compute aggregate values.
Fetch Joins: In addition to the uses of regular joins: Used to fetch related entities and include them in the hydrated result of a
query.
So to get an entity including its associations you will need to "fetch-join" all these associations to make sure they are loaded eagerly.
I usually don't use DQL queries for getting entities and solving my fetch joins, instead I add a custom method to a repository where I use a query builder. This is more flexible and much more readable then using DQL. The correct DQL query will be created by the query builder when we call the createQuery method. You can check the created DQL query of course for debug purposes.
An example for such a custom method inside the Patientprofile entity repository from the question above:
public function findPatientByIdWithAssociations($id)(
// create a query builder for patient with alias 'p'
$qb = $this->createQueryBuilder('p')
->where('p.id = :patient_id')
->addSelect('pd')
->leftJoin('p.documentation', 'pd')
->addSelect('pa')
->leftJoin('p.address', 'pa')
->setParameter('patient_id', $id);
$query = $queryBuilder->getQuery();
return $query->getSingleResult();
}
And now you can use your custom repository method to get the patient by id (for example '555555557') including associations to the patient documentation and address:
$repository = $this->em->getRepository('Entities\Patientprofile');
$patient = $repository->findPatientByIdWithAssociations('555555557');
Make sure you use both addSelect and leftJoin to do eager loading.
Doctrine 2 uses Proxy classes for lazy loading, so you don't actually need to have the associations' data fetched until you use the objects. Since the Proxy classes inherit from your association classes, you're able to use the proxies exactly as you would use the fretch association classes.
but, if you really need to fetch the actual association classes, you need to tell the query to set the fetch mode to Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER. If you're using the annotations, you can achieve this with:
e.g.
/**
* #ManyToMany(targetEntity="Item", fetch="EAGER")
*/
private $items;
You can use a DQL query:
$query = $em->createQuery("SELECT p, f FROM Entities\\Patientprofile p JOIN p.Foo f WHERE p.id = ?1");
$query->setParameter(1, 321);
$patient = $query->getSingleResult();
Faced the same problem.
It was necessary to pull out all chain of parents of an element.
$query->setFetchMode(EntityClass, "alias_in_entity", 3) gets only 1 lvl deep, other parents are just proxy.
This can be fixed by changed in entity class fetch mode to eager. But if it`s not if this is not possible for some reason (performance etc), this can be made as #wormhit mentioned by changing entity metadata "on fly"
Example:
$query = $this->entityManager->createQueryBuilder()->select('fields')
->from(FormField::class, 'fields');
$metadata = $this->entityManager->getClassMetadata(FormField::class);
$metadata->setAssociationOverride('parent', ['fetch' => \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER]);
return $query->getOneOrNullResult();

Categories