Laravel Eloquent Advanced HAVING - mysql

I have a rather strange need from Eloquent. I need to represent the following query using the Eloquent way of doing things. The values come directly from user input so I don't know how best to proceed.
SELECT
GROUP_CONCAT(Table1.table2ID) AS table2ID,
GROUP_CONCAT(Table1.table3ID) AS table3ID
FROM
Table1
HAVING table2ID LIKE '%1%' AND (table3ID LIKE '%123%' OR '%456%')
AND table2ID LIKE '%2%' AND (table3ID LIKE '%789%' OR '%012%')
NOTE: This is an extremely reduced version of query to reduce confusion, but this is the part I am having the issue with.
$query->having(...) doesn't support the closure method that $query->where does.

If your query is too complicated for eloquent, think that the point of eloquent is doing more readable and easy the code. There is no need to overcomplicate yourself, create a scope where you can add a raw query to your Model, like this:
use Illuminate\Database\Eloquent\Model;
class MiModel extends Model
{
public function scopeHasWhatIneed($query)
{
return $query->select(DB::raw("your complex query here"));
}
}
Now you can play with your model, with understable code:
$mi_model->hasWhatIneed()->where(.....)->orderby...
Ah, notice that with raw queries you need to protect yourself from sql injection.

Related

(Spring JPA) Method name query

First of all, sorry for my poor english.
I want to change the following query to 'findBy~' method, but i don't know how to.
#Query(value = "SELECT t FROM Table t WHERE (b.num1 <= :variable or b.num1 IS NULL) AND (b.num2 >= :variable or b.num2 IS NULL)")
Or, is it impossible to get the result by using 'findby~' method name?
I would appreciate if anyone could reply.
Spring Data JPA does have support for all the conditions in your query and nesting of conditions. I'd argue that your query name will become unnecesarelly verbose. It would end up as
Table findByNum1LessThanEqualOrNum1IsNullAndNum2GreaterThanEqualOrNum2IsNull(Integer var0, Integer var1);
This should return the appropiate query, but you'd need to send the variable twice, once for each equals.
With #Query you have the freedom to call your query as you'd like and reuse the same variable.
Now, you CAN fix the downsides of using named methods by using a default method like
default Table myQuery (Integer var) {
return findByNum1LessThanEqualOrNum1IsNullAndNum2GreaterThanEqualOrNum2IsNull(var, var);
}
So you call this instead of the actual query, but then again, it would be much cleaner to use #Query with a proper, descriptive or even self-documenting name if you don't comment your code (you should comment your code). In any case, I suggest you use method names for simple queries and use #Query for anything more complex.
Please, refer to the following links for further reading:
Spring JPA Query Creation
Spring JPA Query Keyword Repository
LeafyJava article on Query Precedence Tricks, which also provides and example of how to change your query logic in case the conditions aren't arranged as you want.
This SO question also provides a bit of insight.

Knex.js Select Average and Round

I am switching an application from PHP/MYSQL to Express and am using knex to connect to the MYSQL database. In one of my queries I use a statement like such (I have shortened it for brevity.)
SELECT ROUND(AVG(Q1),2) AS Q1 FROM reviews WHERE id=? AND active='1'
I am able to use ROUND if I use knex.raw but I am wondering if there is a way to write this using query builder. Using query builder makes dealing with the output on the view side so much easier than trying to navigate the objects returned from the raw query.
Here is what I have so far in knex.
let id = req.params.id;
knex('reviews')
//Can you wrap a ROUND around the average? Or do a Round at all?
.avg('Q1 as Q1')
.where('id', '=', id)
Thanks so much!
You can use raw inside select. In this case:
knex('reviews')
.select(knex.raw('ROUND(AVG(Q1),2) AS Q1'))
Check the docs here for more examples and good practices when dealing with raw statements.

Understanding SQL Aliases

I understand the concept of SQL aliases, but in the following doctrine query language example, the alias is at the front of the table names, as well as following behind them. Could someone help explain whats happening in this query? I want to try to get a better understanding of whats happening before i attempt to alter it.
public function getByDomain($domain)
{
return $this->createQuery('d')->select('d.*, c.*, p.*, cl.*')
->innerJoin('d.Table1 c')
->innerJoin('c.Table2 p')->innerJoin('c.Table3 cl')
->where('d.name=?',$domain)->fetchOne();
}
What is happening is that you are calling $this->createQuery() from inside a method that resides in a class that extends Doctrine_Table. The createQuery() method takes one parameter $alias, and returns a Doctrine_Query object with the 'from' automatically added in (that is why there is no ->from() call in the statement).
The full code probably looks like this:
class DomainTable extends Doctrine_Table
{
public function getByDomain($domain)
{
return $this->createQuery('d')->select('d.*, c.*, p.*, cl.*')
->innerJoin('d.Table1 c')
->innerJoin('c.Table2 p')->innerJoin('c.Table3 cl')
->where('d.name=?',$domain)->fetchOne();
}
}
In Doctrine, the alias can be used in front of the other model names you want to perform a join on. Doctrine will automatically determine the foreign keys if you have the proper relations defined in your schema files.
So this code is selecting all columns from Domain, Table1, Table2 and Table3 where the Domain.name column matches $domain, and only returns 1 result (LIMIT 1).

How to get the model name from the table name in CakePHP

In one of my projects, I have a function :
public function select($table){
$model=Inflector::singularize($table);
$result=$this->$model->find('all'));
...........
...........
}
Here, I tried to get the Model name from the given "table name"($table), and used find function of that model to select all data from that table. But that didn't work.
So, what should I do here ? Can anybody please help me ?
Thanks
You can use my trick. create table name as per model name, or using substr() or strstr() you can extract table prefix, then you can use your model ,
$tableName="myprefix_posts";
$dynamicModelName=strstr($tableName,"myprefix_");
$this->loadmodel($dynamicModelName);
if( $this->$dynamicModelName->save($this->data)){
// your code
}
You can use the Inflector class for the table name to be singularized. Example:
$dynamicModel = Inflector::singularize($table);
When you want to use the model via $this->Model you have to load it first.
$this->loadmodel($dynamicModel);
Just a friendly warning: Try not to use words that might be keywords in PHP or MySQL as variable or function names like you do in the function above (select). You or any other developers might get confused at later stages of development. There is probably no harm, I just don't recommend it.
Enjoy

Creating an OR statement using existing conditions hash

I am working on a problem where I need to add an OR clause to a set of existing conditions. The current conditions are built in a hash in a method and at the end, they are used in the where clause. Here is a simplified example:
...
conds.merge!({:users => {:archived => false}})
Model.where(conds)
I am trying to add an OR clause to the current set of conditions so it would be something like '(conditions) OR new_condition'. I'd like to add the OR statement without converting each addition to the conds hash into a string. That would be my last option. I was hoping someone has done something like this before (without using Arel). I seem to recall in Rails 2 there was a way to parse a conditions hash using a method from the model (something like Model.some_method(conds) would produce the where clause string. Maybe that would be a good option to just add the OR clause on to that string. Any ideas are appreciated. Thank you for your help!
I found a way to do what I needed. Instead of changing all of the conditions that I am building, I am parsing the conditions to SQL using sanitize_sql_for_conditions. This is a private method in ActiveRecord, so I had to put a method on the model to allow me to access it. Here is my model method:
def self.convert_conditions_hash_to_sql(conditions)
self.sanitize_sql_for_conditions(conditions)
end
So, once I convert my conditions to text, I can add my OR clause (along with the appropriate parentheses) to the end of the original conditions. So, it would go something like this:
Model.where('(?) OR (model.type = ? AND model.id IN(?))', Model.convert_conditions_hash_to_sql(conds), model_type, model_id_array)