Doctrine Query builder - how to select based on field inside JSON column - php

I have a table with a JSON column, it's type longtext (DC2Type:json). This table has an Entity in Doctrine ORM in my Symfony project. I would like to query based on fields inside the JSON column, using the Doctrine query builder I have in a variable $qb
How do I do this? Everything I found online says to install a 3rd-party package to enable this. Is there no way to just do it with Doctrine's query builder without installing another package?
One (maybe dumb) workaround I tried was to treat the column as a string, and do...
$qb->andWhere("my_data LIKE \"%id:\\\"1,%\"");
For example, if I wanted to query the JSON column my_data to find the blobs that contain id":1, in the string. This fails with a very strange syntax error, and isn't the right way to query a JSON field anyway. HOWEVER, doing the LIKE query directly in SQL client works the way I want, so I also don't know why this is failing in Doctrine.
EDIT: This is MySQL / MariaDB.

Doctrine Query Language is pretty limited. It covers only the most basic/common SQL functions, which is enough for like 99% use cases, but not all.
If you have a MariaDB version that natively supports JSON (so 10.2 or later) you can use native functions to work with the JSON data. (If you don't then your workaround is the only option regardless, with perhaps some additional filtering in the application).
To be able to use these functions in DQL you either need to define them yourself or indeed use a third party library like scienta/doctrine-json-functions (note that it has documentation for how to use it with Symfony, and it's really simple).
If you need just a single extra function and for some reason don't want the whole bundle, you could just copy that single class and use it as your own.
Alternatively you can forgo DQL and write SQL directly, but that way you can't hydrate into objects directly and use other Doctrine magic with the data. But it can be enough for simple use cases.

Related

zend framework muti-table query prototype

The tutorial of zend framework show that I can use Zend\Db\ResultSet\HydratingResultSet to return a model object using the dbAdapter ,select query and a model prototype. But in most case writing the website code I use multi-table query and the hydrator can not fix this problem but just a single table query. How should I deal with this kind of problem.
hydrator doc
And this problem can also happen when encounting the pagination which also only take one prototype.
paginator doc
Depending on the query, you might need a custom ResultSet that knows how to deal with the multi-row potential of a multi table query, and mapping that dataset appropriately.
Additionally, if you find yourself getting into very advanced object relationships with Zend\Db, that might be the time to start considering Doctrine2 (or a similar ORM).

Laravel Eloquent: How does it work? Based on dynamic Schema MetaData-Lookup = Performance?

The whole persistance layer is a rather big and complex laravel component. Is there someone who has already looked at it in depth and can explain it in a few words, whats going on schematically under the hood?
Eloquent (in contrast to other ORM Layers) seems to not cache/store the Column Metadata somewhere in the application? Or does it? (In Java JPA or Doctrine this is done via Annotations, but laravel seems to not have this metadata?)
This would mean that it has to query on every request the INFORMATION_SCHEMA.COLUMNS from MySQL. Does this mean, that laravel will for every simple SQL query have to iusse two queries (the first one to get the column/table definitions?)
Will calling hasColumn on a new Instance/Facade inevitably lead to an SQL Query, just to retrieve the metadata/table-definition?
As far as i know, you are right, Eloquent does not cache or store the column metadata. But this does not mean that each query requires a second on which requests the information schema. The query builder just assumes that you know which column names are legal and creates and sends the query. This means, that if you select an unknown column, you'll get an exception! Finally hasColumn leads to an SQL query.

With Doctrine what are the benefits of using DQL over SQL?

Can someone provide me a couple clear (fact supported) reasons to use/learn DQL vs. SQL when needing a custom query while working with Doctrine Classes?
I find that if I cannot use an ORM's built-in relational functionality to achieve something I usually write a custom method in the extended Doctrine or DoctrineTable class. In this method write the needed it in straight SQL (using PDO with proper prepared statements/injection protection, etc...). DQL seems like additional language to learn/debug/maintain that doesn't appear provide enough compelling reasons to use under most common situations. DQL does not seem to be much less complex than SQL for that to warrant use--in fact I doubt you could effectively use DQL without already having solid SQL understanding. Most core SQL syntax ports fairly well across the most common DB's you'll use with PHP.
What am I missing/overlooking? I'm sure there is a reason, but I'd like to hear from people who have intentionally used it significantly and what the gain was over trying to work with plain-ole SQL.
I'm not looking for an argument supporting ORMs, just DQL when needing to do something outside the core 'get-by-relationship' type needs, in a traditional LAMP setup (using mysql, postgres, etc...)
To be honest, I learned SQL using Doctrine1.2 :) I wasn't even aware of foreign-keys, cascade operations, complex functions like group_concat and many, many other things. Indexed search is also very nice and handy thing that simply works out-of-the-box.
DQL is much simpler to write and understand the code. For example, this query:
$query = ..... // some query for Categories
->leftJoin("c.Products p")
It will do left join between Categories and Products and you don't have to write ON p.category_id=c.id.
And if in future you change relation from one-2-many to let's say many-2-many, this same query will work without any changes at all. Doctrine will take care for that. If you would do that using SQL, than all the queries would have to be changed to include that intermediary many-2-many table.
I find DQL more readable and handy. If you configure it correctly, it will be easier to join objects and queries will be easier to write.
Your code will be easy to migrate to any RDBMS.
And most important, DQL is object query language for your object model, not for your relational schema.
Using DQL helps you to deal with Objects.
in case inserting into databae , you will insert an Object
$test = new Test();
$test->attr = 'test';
$test->save();
in case of selecting from databae, you will select an array and then you can fill it in your Object
public function getTestParam($testParam)
{
$q=Doctrine_Query::create()
->select('t.test_id , t.attr')
->from('Test t ')
$p = $q->execute();
return $p;
}
you can check the Doctrine Documentation for more details
Zeljko's answer is pretty spot-on.
Most important reason to go with DQL instead of raw SQL (in my book): Doctrine separates entity from the way it is persisted in database, which means that entities should not have to change as underlying storage changes. That, in turn, means that if you ever wish to make changes on the underlying storage (i.e. renaming columns, altering relationships), you don't have to touch your DQL, because in DQL you use entity properties instead (which only happen to be translated behind the scenes to correct SQL, depending on your current mappings).

PHP get MongoDBRef with query

I was wondering if there is a way to perform a find() and have Mongo automatically return the associated references without having to run getDBRef() once the parent record has been returned.
I don't see it anywhere in the PHP documentation. I can easily support using getDBRef but it doesn't seem as efficient as it could be.
Also...I'm surprised there's no way to select the specific data to return in the linked reference. I may as well just perform another manual find statement so I can control what the return is...but there has to be a more performance oriented way to do this.
Perhaps I should change my methodology and instead of using the PHP library classes for find, generate my own JavaScript command and run it using the MongoCode class? Would that work and if so...I'm wondering what it would look like. scratches head then heads to The Google
Thanks!
MongoDB does not support joins. Database References (DBRefs) just refers to the practice of a field storing an _id referencing another document. There is currently no specific server-side support for this, and hydrating the reference to a document does require another query. Some MongoDB drivers have convenience methods so you don't have to manually do the find. It is equally valid/performant if you want to do your own find() given a DBRef to lookup (or use other criteria to find related documents).
Depending on your use case and data modelling, a more efficient alternative to the DBRef linking could be embedding related data as a subdocument. See the MongoDB wiki info on Schema Design for more examples.
As far as performance goes, it would be better to use PHP queries than MongoCode (JavaScript which needs to be eval'ed on the server). MongoCode is really intended for more limited use such as within Map/Reduce functions. Refer to Server-Side Code Execution for some of the potential limitations with that approach.
Refer: http://docs.mongodb.org/manual/reference/database-references/
Manual references where you save the _id field of one document in another document as a reference. Then your application can run a second query to return the related data. These references are simple and sufficient for most use cases.
DBRefs are references from one document to another using the value of the first document’s _id field, collection name, and, optionally, its database name. By including these names, DBRefs allow documents located in multiple collections to be more easily linked with documents from a single collection.
To resolve DBRefs, your application must perform additional queries to return the referenced documents. Many drivers have helper methods that form the query for the DBRef automatically. The drivers do not automatically resolve DBRefs into documents.
So either way, no matter which type of referencing you are using, you need to do the dereferencing yourself.
Hope it helps!

Need a simple ORM or DBAL for existing PHP app

I am working on extending an existing PHP application. Unfortunately for me, the existing app is a mess. It's all spaghetti code with raw mysql_* calls. Groan. No way that I am going to do that in the parts that I am extending.
So, I am looking for a simple ORM of DBAL that I can easily drop in and start using. Desired features:
It must work on an existing database schema. Preferably with minimal or no additional configuration. The existing database schema is the same quality as the existing PHP code (no sensible naming conventions, not normalised, etc.). I don't want to spend days converting the database schema manually into annotated object properties a la Doctrine 2.
It must be able to work alongside the existing raw mysql_* queries. I have no idea how hydrating ORMs like Doctrine 2 or Propel behave when scripts are manually manipulating the data in the database behind their backs, but I assume it's not pretty.
It must run on PHP 5.2.x. I'd love to use PHP 5.3 but I have zero interest in going over the existing 125K lines of spaghetti code mess to make sure it runs on PHP 5.3.
Relationships not required. In the few places I need to get to relational data, I'll be happy to call an extra find() or query() or whatever myself.
Bonus points if it has some trigger support (e.g. beforeSave, afterSave). Not a requirement, but just nice to have.
Edit: Someone put me out of my misery. I just found out that the 125K lines of spaghetti code also changes the database schema. E.g, add an extra option somewhere and a whole slew of ALTER TABLE statements start flying. I could probably fill a year's worth of TheDailyWTF with this codebase. So, one more requirement:
Must be able to cope with a changing database schema automatically (e.g. adding columns).
I have been looking at a few solutions, but I am unsure how well they would work given the requirements. Doctrine 2, RedBeanPhp and the like all require PHP 5.3, so they are out. There's a legacy version of RedBeanPhp for PHP 5.2.x but I don't know if it would work with a messy, existing database schema. NotORM looks okay for getting data out but I don't know if it can be configured for the existing database schema, and how you can easily put data back into the database.
Ideally I would like something simple. E.g:
$user = User::find($id);
$user->name = 'John Woo';
$user->save();
Or:
$articles = ORM::find('article')->where('date' => '2010-01-01');
foreach ($articles as $article) {
echo $article->name;
}
Any tips or even alternative solutions are welcome!
I use...
http://github.com/j4mie/idiorm/
it has an active record implementation too in the form of Paris.
With regard to your edit. Idiorm copes with changing schemas and the syntax almost exactly matches the type you want in your question.
How well did you look into Doctrine? I am using Doctrine 1.2 for these kind of things. Quite easy to setup, allows you to start off with an existing schema. It automatically figures out the relations between tables that have foreign key constraints.
It has extensive trigger and behaviour support, so the bonus points can be spent as well, and it has relational support as well, so your additional queries are not necessary. It has beautiful lazy loading, and it comes with a flexible query language (called DQL) that allows you to do almost exactly the same stuff that you can do in SQL in only a fraction of the effort.
Your example will look like this:
/* To just find one user */
$user = Doctrine::getTable('User')->findOneById($id);
/* Alternative - illustrating DQL */
$user = Doctrine_Query::create()
->from('User u')
->where('u.id = ?',array($id))
->fetchOne();
$user->name = 'John Woo';
$user->save();
It must be able to work alongside the existing raw mysql_* queries. I have no idea how hydrating ORMs like Doctrine 2 or Propel behave when scripts are manually manipulating the data in the database behind their backs, but I assume it's not pretty.
Well, that is technically impossible to auto-manage; a SQL database is simply not pushing back stuff to your ORM, so to update stuff that was changed in the background, you need to perform an additional query one way or the other. Fortunately, Doctrine makes this very easy for you:
/* #var User $user */
/* Change a user using some raw mysql queries in my spaghetti function */
$this->feedSpaghetti($user->id);
/* Reload changes from database */
$user->refresh();

Categories