multiple joins with the same table in eloquent - sql

Is there some elegant way to write the following query in eloquent? I would like to avoid using the raw query:
results(id, data)
result_filters (id, result_id, filter_id, value)
SELECT * FROM results, result_filters as age, result_filters as followers
WHERE age.result_id = results.id
AND followers.result_id = results.id
AND age.filter_id = 2
AND age.value > 90
AND followers.filter_id = 6
AND followers.value < 10000

If you are using the laravel eloquent
your model relation could be like
model Result:
public function resultFilters()
{
return $this->hasMany('App\ResultFilter');
}
model ResultFilter:
public function result()
{
return $this->belongsTo('App\Result','result_id');
}
try the below code:
$result = Result::find(1);
$data = $result->resultFilter()
->whereIn('filter_id',[2,6])
->whereBetween('value',[90,10000])
->get();

What do you think? It's uses DB::raw but it works
$data = DB::table(DB::raw("results, result_filters as age, result_filters as followers "))
->select(DB::raw("* "))
->where('age.result_id', 'results.id')
->where('followers.result_id', 'results.id')
->where('age.filter_id', 2)
->where('age.value', '<', 90)
->where('followers.filter_id',6)
->where('followers.value','<',10000)
->get();

Related

Laravel Query: implement Eloquent Scope for Query Builder

In the Laravel Query Builder I want to implement something like Scope in Eloquent.
Ref: Laravel Queries: Adding custom feature like Soft Deletes.
I have some complex queries (with joins and what not) but I want to be able to easily apply a WHERE condition that works as follows:
original:
Select * from t1 join t2 ... join t3 ... etc
Where t1.c1 = x OR t3.c4 like "%like"
wanted:
Select * from t1 join t2 ... join t3 ... etc
Where (t1.c1 = x OR t3.c4 like "%like") AND (t1.isTest = false AND t3.isTest = false)
I have written the following method:
public static function scopeNoTest($query, $tables=[false])
{
if (!is_array($tables)) $tables = [$tables];
foreach ($tables as $table)
{
$field = ($table) ? $table . '.isTest' : 'isTest';
$query = $query->where(function ($q) use ($query, $field)
{
$q->where($field, false)
->orWhereNull($field);
}
);
}
return $query;
}
It gets run like this:
$select = <parameter driven select statement>
$where[$role] = <array of different where condition based on passed in parameter?
$bindings = <query bindings based on passed in parameters>
$query = DB::table('Transactions AS trans')
->leftJoin('Buyers AS b', 'trans.ID', '=', 'b.Transactions_ID')
->leftJoin('Sellers AS s', 'trans.ID', '=', 's.Transactions_ID')
->leftJoin('Agents AS ba', 'trans.BuyersAgent_ID', '=', 'ba.ID')
->leftJoin('Agents AS sa', 'trans.SellersAgent_ID', '=', 'sa.ID')
->leftJoin('TransactionCoordinators AS btc', 'trans.BuyersTransactionCoordinators_ID', '=', 'btc.ID')
->leftJoin('TransactionCoordinators AS stc', 'trans.SellersTransactionCoordinators_ID', '=', 'stc.ID')
->leftJoin('lu_UserRoles AS lu_ur', 'trans.ClientRole', '=', 'lu_ur.Value')
->leftJoin('Properties AS p', 'trans.Properties_ID', '=', 'p.ID')
->selectRaw($select);
// ... Adds code to Only Select records with isTest NOT True
$query = Model_Parent::scopeNoTest($query, ['trans', 'ba', 'sa', ]);
$query->whereRaw($where[$role].$whereUser, $bindings)->distinct();
$transactions = $query->get();
The problem with this code is that it does not put the original [passed in] query in parentheses - so the query is wrong!.
The WHERE the code creates is:
where
(`trans`.`isTest` = 0 or `trans`.`isTest` is null)
and (`ba`.`isTest` = 0 or `ba`.`isTest` is null)
and (`sa`.`isTest` = 0 or `sa`.`isTest` is null)
and trans.BuyersTransactionCoordinators_ID = 1 OR trans.SellersTransactionCoordinators_ID = 1
OR trans.CreatedByUsers_ID = 1 OR trans.OwnedByUsers_ID = 1
And I want
where
(`trans`.`isTest` = 0 or `trans`.`isTest` is null)
and (`ba`.`isTest` = 0 or `ba`.`isTest` is null)
and (`sa`.`isTest` = 0 or `sa`.`isTest` is null)
and (trans.BuyersTransactionCoordinators_ID = 1 OR trans.SellersTransactionCoordinators_ID = 1
OR trans.CreatedByUsers_ID = 1 OR trans.OwnedByUsers_ID = 1)
Is there a way to do this ??
It looks like the following line is causing this:
$query->whereRaw($where[$role].$whereUser, $bindings)->distinct();
I think there are two ways to solve this:
// 1
->whereRaw('(' . $where[$role].$whereUser . ')', $bindings)->
// 2
->where(function ($query) use (...) {
$query->whereRaw(...);
})->

Query giving double result instead of single

I have two tables: products and current_product_attribute_values
I have tried a join query to filter them as per attribute selected by the user but when I try this with an additional condition it gives me 2 results instead of one it is including the first one which is not matching as per query:
select * from `products` inner join `current_product_attribute_values` on `products`.`id` = `current_product_attribute_values`.`product_id` where `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `product_name` LIKE ?
here is my laravel Controller code :
$all = Input::all();
$q = Input::get('search_text');
$att_val = Input::get('attribute_value');
$subcat = Input::get('subcat_id');
$subcat_name = DB::table('subcategories')->where('id', $subcat)->value('subcategory_name');
$brandname = DB::table('brands')->where('subcat_id', $subcat)->value('brand_name');
$brand_id = DB::table('brands')->where('subcat_id', $subcat)->value('id');
$product_count = DB::table('products')->where('brand_id', $brand_id)->count();
if ($q != "") {
// getting multiple same name params
$query = DB::table('products');
$query->join('current_product_attribute_values', 'products.id', '=', 'current_product_attribute_values.product_id');
$j = 0;
foreach ($all as $key => $values) {
//echo 'my current get key is : ' . urldecode($key). '<br>';
if ($key == $name[$j]) {
$query->where('current_product_attribute_values.attribute_id', '=', $att_id_value[$j]);
echo'<br>';
print_r($query->toSql());
echo'<br>';
//echo '<br> key matched and have some value : <br>';
//echo count($values);
if (count($values) >= 1) {
//echo '<br> it has array inside <br>';
foreach ($values as $val) {
// or waali query in same attribute
echo'<br>';
$query->orwhere('current_product_attribute_values.attribute_value_id', '=', $val);
print_r($query->toSql());
echo'<br>';
}
}
$j++;
}
}
$records = $query->toSql();
$query->where('product_name', 'LIKE', '%' . $q . '%');
$records = $query->toSql();
print_r($records);
$products = $query->paginate(10)->setPath('');
$pagination = $products->appends(array(
'q' => Input::get('q')
));
if (count($products) > 0) {
$filters = DB::table('product_attributes')->where('subcategory_id', $subcat)->get(['attribute_title']);
} else {
$filters = array();
}
$categories = categories::where('add_to_menu', 1)->with('subcategories')->with('brands')->get();
$categoryhome = categories::where('add_to_menu', 1)->with('subcategories')->get();
return view('searchfilter')
->with('productsdata', $products)
->with('filtersdata', $filters)
->with('categories', $categories)
->with('categorieshome', $categoryhome)
->with('subcat_name', $subcat_name)
->with('subcat_id', $subcat)
->with('brandname', $brandname)
->with('product_count', $product_count)
->with('querytext', $q);
}
return 'No Details found. Try to search again !';
its easier if you use raw sql as calling db select function. ex:
$query=DB::select("select * from `products` inner join `current_product_attribute_values` on `products`.`id` = `current_product_attribute_values`.`product_id` where `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `product_name` LIKE ?
");
indeed you can concat vars in raw sql if you need to, ex:
$queryBrands = "select id from brands where subcat_id =".$subcat;
//echo $queryBrands
$queryBrands = DB::select($queryBrands);
By looking at your tables, product table with id value 17 has two records in table current_product_attribute_values in column product_id (I assume this column is used as foreign key to product table).
With select *, you select all of the columns from both tables. So it would most likely cause your query to return multiple records.
My suggestions:
Only select the columns you need. Avoid using select * in the long run, i.e. select product.id, product.description, current_product_attribute_values.attribute_values ......
Make use of GROUP BY
Hope these helps.

Doctrine query with orderBy count

I have an Entity "Company" (linked to an "Account") which can have multiple "Tickets".
I want to get the five first companies with the most tickets
I've got this so far but it doesn't work :
public function getByMostTickets($idAccount) {
$qb = $this->createQueryBuilder('c')
->where("c.account = ".$idAccount)
->orderBy("COUNT(c.tickets)", "DESC");
return $qb->getQuery()->getResult();
}
Apparently, it doesn't like that I do the "COUNT" in the orderBy.
Can you try something like this?
$qb = $this->createQueryBuilder('c')
->join("c.tickets", "t")
->where("c.account = :account_id")
->setParameter("account_id", $idAccount)
->groupBy("c.id")
->orderBy("COUNT(t.id)", "DESC");
[EDITED]
sorry this should work... But dump the result to see what this returns...
$qb = $this->createQueryBuilder('c')
->select("c", "COUNT(t.id) as countTickets")
->join("c.tickets", "t")
->where("c.account = :account_id")
->setParameter("account_id", $idAccount)
->groupBy("c.id")
->orderBy("countTickets", "DESC");

Yii db command and CDbCriteria

I have two tables Products(id, name) and Views(id,count,time), and those two tables are not related to each other. This is my code:
$dbCommand = Yii::app()->db->createCommand("
SELECT P.`id`, P.`name`, V.`time`
FROM `products` P, `views` V
WHERE P.`type` = 2
ORDER BY V.`time` DESC
");
$data = $dbCommand->queryAll();
It is working, but I want to convert this query to CDbCriteria syntax.
$cdb = new CDbCriteria();
$cdb->select = //???
$cdb->where = //???
$cdb->order = //???
How can I do this? Can somebody help me?
You cannot use CDbCriteria, Try using query builder.
Yii::app()->db->createCommand()
->select('P.id, P.name, V.time')
->from('products P, views V')
->where('P.type = :type')
->order('V.time DESC')
->queryAll(array(
':type' => 2
));

How to get particular column in zend using Left join

I am new to zend framework,
Following is the plain mysql query which takes particular column from table,
SELECT jobs_users.id,jobs_users.first_name from jobs_users left join friends on jobs_users.id=friends.friend_id where friends.member_id=29
I tried with zend to implement the above query like below,
public function getFriendsProfileList($id){
$db = Zend_Db_Table::getDefaultAdapter();
$select = $db->select();
$select->from('jobs_users')
->joinLeft(
'friends',
'jobs_users.id=friends.friend_id',
array('jobs_users.id','jobs_users.first_name','jobs_users.last_name','jobs_users.photo')
)
->where("friends.member_id = ?", $id);
$result = $db->fetchAll($select);
return $result;
}
Here i got result with all column name , not with exact column name which i have given in query.
Kindly help me on this.
Use this instead:
$select->from('jobs_users', array('jobs_users.id','jobs_users.first_name','jobs_users.last_name','jobs_users.photo'))
->joinLeft('friends', 'jobs_users.id=friends.friend_id')
->where("friends.member_id = ?", '20');
You may also try this:
$select = $db->select();
$select->setIntegrityCheck(false);
$select->joinLeft('jobs_users','',array('jobs_users.id','jobs_users.first_name','jobs_users.last_name','jobs_users.photo'));
$select->joinLeft('friends','jobs_users.id=friends.friend_id', array());
$select->where("friends.member_id = ?", $id);
$result = $db->fetchAll($select);
return $result;