Symfony 1.4 sfDoctrinePager: How to set custom SQL? - php

There is an old project written in symfony 1.4
And there is SQL that it is not possible to create using Doctrine. For an example, consider this test SQL:
SELECT id,name FROM users
UNION ALL
SELECT id*10 as id,name FROM users
puvlic function getPager(){
$sql = "SELECT id,name FROM users
UNION ALL
SELECT id*10 as id,name FROM users";
$pager = new sfDoctrinePager('Users', 10);
$pager->setQuery($sql);
$pager->setPage(2);
$pager->init();
return $pager;
}
It gives the following error:
Fatal error: __clone method called on non-object in ...
Help please solve the problem.

With stock sfDoctrinePager class it's not possible to use a custom SQL string. It only accepts an instance of Doctrine_Query (see sfDoctrinePlugin sources).
You should construct a Doctring_Query object that does exactly what you need (which is likely not possible because of UNION). It requires a query object because pager needs to modify query before executing it (add LIMIT and OFFSET) in a platform-independent way (for example, it's different on MySQL vs. MS-SQL).
If you really need to use custom SQL with UNION, the only way to achieve it is to implement your custom sfDoctrinePager implementation that accepts SQL and assumes your target DB platform, certain SQL query structure, etc.

Related

Is there a way to add a LIMIT to an UPDATE query in Doctrine ORM?

I am using Doctrine 2.5.x and I am having problems with getting the LIMIT clause to work for UPDATE queries. It always updates all matched records (i.e. it seems to ignore the LIMIT clause).
setMaxResults() seems to have no effect when used together with UPDATE queries.
As a quick workaround I am using a native MySQL query but that cannot be the best solution.
I tried these examples but none are working:
Doctrine update query with LIMIT
https://recalll.co/app/?q=doctrine2%20-%20Doctrine%20update%20query%20with%20LIMIT
QueryBuilder with setMaxResults() (does not work):
$qb = $em->createQueryBuilder();
$query = $qb->update('\Task\Entity', 't')
->set('t.ClaimedBy', 1)
->where('t.Claimed IS NULL')
->getQuery();
$query->setMaxResults(20);
$this->log($query->getSQL());
Hope someone can help in finding a better solution than a native query. It takes away the whole benefit of the ORM.
Is it even possible to use a LIMIT clause in an UPDATE statement?
In short, no, because the SQL specification does not support UPDATE ... LIMIT ..., so none of the ORM trying to achieve portability should allow you to do it.
Please also have a look at MySQL Reference Manual itself stating that UPDATE ... LIMIT ... is not a standard SQL construction:
MySQL Server supports some extensions that you probably will not find in other SQL DBMSs. Be warned that if you use them, your code will not be portable to other SQL servers. In some cases, you can write code that includes MySQL extensions, but is still portable, by using comments of the following form:
SQL statement syntax
The ORDER BY and LIMIT clauses of the UPDATE and DELETE statements.
So by essence because what you are trying to achieve is not standard SQL the ORM will not have a portable way to implement it and will probably not implement it at all.
Sorry, but what you are trying to achieve is not possible through DQL, because:
Ocramius commented on Sep 2, 2014
DQL doesn't allow limit on UPDATE queries, as it is not portable.
As suggested in this issue of DoctrineBundle repository by its owner, Marco Pivetta (he also happen to be the owner of the ORM repository).
Further information, although it might needs a good link to the right ISO specification documentation that is sadly not freely available:
The ISO standard of UPDATE instruction do not allow LIMIT in an UPDATE, where SELECT is, of course, an instruction that does allow it.
As you were raising it by yourself, the purpose of an ORM is to not write pure SQL in order to have it cross DBMS compatible. If there is no possibility to make it that way, then it makes sense that the ORM does not implement it.
Also note that on other SQL variant than MYSQL, the limit is actually part of the SELECT clause:
select * from demo limit 10
Would translate in a SQL Server to
select top 10 from demo
Or in Orcale to
select * from demo WHERE rownum = 1
Also see: https://stackoverflow.com/a/1063937/2123530
As b.enoit.be already stated in his answer, this is not possible in Doctrine because using LIMIT's in an UPDATE statement is not portable (only valid in MySQL).
Hope someone can help in finding a better solution than a native query. It takes away the whole benefit of the ORM.
I would argue that you are mixing business rules with persistence (and the ORM does not play well with that, luckily).
Let me explain:
Updating an entity's state is not necessarily a business rule. Updating max. 20 entities is (where does that 20 come from?).
In order to fix this, you should properly separate your business rules and persistence by separating it into a service.
class TaskService
{
private $taskRepository;
public function __construct(TaskRepository $taskRepository)
{
$this->taskRepository = $taskRepository;
}
public function updateClaimedBy()
{
$criteria = ['Claimed' => null];
$orderBy = null;
// Only update the first 20 because XYZ
$limit = 20;
$tasks = $taskRepository->findBy($criteria, $orderBy, $limit);
foreach($tasks as $task) {
$task->setClaimedBy(1)
}
}
}

Codeigniter 3 Oracle Set Query Builder Case Insensitivity

Using Codeigniter 3 at work with Oracle 12c and sporadic uses of the Query Builder class.
Query Builder is selecting from Oracle with escaped table names. I would like to configure the class to not do this. I'd like this call:
$query = $this->db->get('customers');
to go from
select * from 'customers';
to
select * from customers;
in generated SQL.
Is there a config item I don't know about or am I going to have to fix one of my coworker's queries every three weeks until the end of time?
You have to use next syntax
$this->db->select('SELECT * FROM customers', false)->get();
since second parameter in select($tablename, $escape=NULL) method will prevent table name or field names escaping when set to FALSE.

Builder for ElasticSearch query DSL in Yii2 (or standalone)

As far as I can see, the only way to make a query to ElasticSearch in Yii2 is to run ElasticModel::find()->query($query), where $query is a complex array containing the actual query written in ElasticSearch query DSL.
The query is huge and quickly becomes unmanageable. For SQL Yii2 provides a powerful query builder class that supports tons of useful methods like andWhere(). For ElasticSearch everything goes into one gigantic query expression, very much like building an SQL expression string by hand.
Is there any high-level wrapper for ElasticSearch query DSL for Yii2? If not, is there a standalone library with similar functionality?
If you intend to build for version 1.6 of elastic, I have created a query builder for my company and published it here
You will use it as a standalone query builder, and at the end you will need to get the final query array and pass it to the query executer.
To install it, you can simply use composer composer require itvisionsy/php-es-orm or download the zipped version here.
The link above contains some examples, and here is a copy:
//build the query using different methods
$query = \ItvisionSy\EsMapper\QueryBuilder::make()
->where('key1','some value') //term clause
->where('key2',$intValue,'>') //range clause
->where('key3','value','!=') //must_not term clause
->where('key4', ['value1','value2']) //terms clause
->where('email', '#hotmail.com', '*=') //wildcard search for all #hotmail.com emails
->sort('key1','asc') //first sort option
->sort('key2',['order'=>'asc','mode'=>'avg']) //second sort option
->from(20)->size(20) //results from 20 to 39
->toArray();
//modify the query as you need
$query['aggs']=['company'=>['terms'=>['field'=>'company']]];
//then execute it against a type query
$result = TypeQuery::query($query);
//i am not sure about Yii way to execute, according to the question, it should be:
$result = ElasticModel::find()->query($query);
The package also include a simple ElasticSearch ORM class which maybe useful for you. Take a look at it here.
Hope this helps you...

Query a Query - MySQL and PHP

I was recently trying to do a project*, which caused me to ask this question. Although since then I've found an alternative solution, I am still curious if what I envisioned doing is, in any way, possible.
Essentially, I am wondering if there is anyway to perform a MySQL query on a MySQL query result in php. For example:
$result = mysql_query("SELECT * FROM foo WHERE bar=".$barValue);
AND THEN, be able to perform multiple queries on $result:
$newResult = mysql_query("SELECT * FROM $result WHERE otherBar=".$barValue);
OR
$otherNewResult = mysql_query("SELECT * FROM $result WHERE otherOtherBar=".$barValue." ORDER BY foobar ASC");
AND so on and so forth...
I realize that I could append the original query with my new WHERE statements and ORDER BYs, but that causes my to query the database unnecessarily and it prevents me from writing more objected oriented code (because I can't pass around a result to be queried, but rather have to requery the database in every function...)
Any advice, pieces of code, frameworks, or ramblings appreciated.
*BTW, my project was having to query a large database of people for people born in certain age groups and then query those age groups for different demographics.
Edit
No, writing a custom function to query the database is not worth the object-orientation (and modifiability) it would give me
You could do a nested query in the same SQL query and keep PHP out of it:
'SELECT * FROM (SELECT * FROM foo WHERE bar="something") AS q1 WHERE q1.bar2 = "something else"'
The question has already been answered. However following explanation will help someone who might be interested in knowing the details of it.
What are Nested query / subquery:
Subqueries are also known as nested queries. A subquery is a SELECT statement within another statement. MySQL supports all SQL standards and additionally provides MySQL specific features.
Why should I use Subquery:
Subquery is structured and it is possible to isolate each parts of statement
Subquery is more readable that complex joins and unions
Subquery provides alternative means to perform action which otherwise would require complex joins and unions
What Subquery returns:
A subquery can return a single value, a single row, a single column, or a table. These are called scalar, column, row, and table subqueries.
Reference: http://dev.mysql.com/doc/refman/5.7/en/subqueries.html
http://www.w3resource.com/sql/subqueries/nested-subqueries.php

Dynamic SQL queries in code possible?

Instead of hard coding sql queries like Select * from users where user_id =220202 can these be made dynamic like Select * from $users where $user_id = $input.
Reason i ask is when changes are needed to table/column names i can just update it in one place and don't have to ask developers to go line by line to find all references to update. It is very time consuming. And I do not like the idea of exposing database stuff in the code.
My major concern is load time. Like with dynamic pages, the database has to fetch the page content, same way if queries are dynamic first system has to lookup the references then execute the queries, so does it impact load times?
I am using codeignitor PHP.
If it is possible then the next question is where to store all the references? In the app, in a file, in the DB, and how?
---EDIT:
Even better: Can the SQL query itself be made dynamic? I can just reference $sqlA instead of the whole query? This way if I have to re-write the query I can just update 1 file.
Because you are using Codeigniter, I would reccomend utilizing the Active Record Class to accomplish what you are trying to do.
The active record class enables you to build queries dynamically in steps allowing you to build them logically. So to take your example using active record...
( this could be accomplished with less code, I'm just trying to illustrate Active Record )
$this->db->select('*');
$this->db->from($table);
$this->db->where($user_id, $input);
and so to show what I mean about building the query logically, you can build whatever logic you want INTO the query building process. Lets say you have a $limit variable that you set if you want to limit the number of results you get. BUT if it isn't set (or NULL) you don't want to set the limit clause.
if ( $isset($limit) ) {
$this->db->limit($limit);
}
and now to execute your query now that it has been built
$query = $this->db->get();
Then just deal with $query with your database class just like you would any other CodeIgniter query object.
Of course you can, if that's what you wish. I'd rather recommend you taking more time to design you database but changes in the schema are inevitable in the long run.
I don't think load time would be an issue with this because ussually the bottleneck in this applications is in the database.
Finally my recommendation is to save this in a file just by declaring the column names as php variables
It depends on the database driver(s) you are using. The old PHP database drivers did not support placeholders (PHP 3.x). The modern (PDO) ones do. You write the SQL with question marks:
SELECT * FROM Users WHERE User_ID = ?
You then provide the value of the user ID when you execute the query.
However, you cannot provide the column name like this - only values. But you could prepare a statement from a string such as:
SELECT * FROM Users WHERE $user_id = ?
Then you provide the value at execute time.
mysql_query() takes a string and it doesn't need to be a constant string, it can be a variable.
$SQL = "SELECT foo FROM bar b";
SQLSet = mysql_query($SQL);
Aa you can see, you can use ordinary string manipulation to build your whole SQL query.
$SQL="SELECT * FROM MyTable";
$BuzID = 5;
$Filter = "Buz=".$BuzID;
if (is_numeric($BuzID)) SQL .= " WHERE ".$Filter;
SQLSet = mysql_query($SQL);
This will expand to "SELECT * FROM MyTable WHERE Buz=5" if $BuzID is set to any number.
If not the statement will just be "SELECT * FROM MyTable"
As you can see, you can build very complex SQL statements on the fly without need of variable support in the SQL server.
IF you want constants such as database name, user login, you can but them in a separate include located outside the public directory.
SecretStuff.inc.php
$__DatabaseName = "localhost";
$__UserName = "DatabaseAccess";
$__Password = "E19A4F72B4AA091C6D2";
Or have the whole PHP database connection code in the same file.

Categories