Laravel 4 Method Improvement - mysql

I have this index method:
public function index()
{
// In the view, there are several multiselect boxes (account managers, company names and account types). This code retrives the values from the POST method of the form/session.
$company_names_value = Input::get('company_names_value');
$account_managers_value = Input::get('account_managers_value');
$account_types_value = Input::get('account_types_value');
// If there has been no form submission, check if the values are empty and if they are assign a default.
// Essentially, all of the records in the table column required.
if (is_null($company_names_value))
{
$company_names_value = DB::table('accounts')
->orderBy('company_name')
->lists('company_name');
}
if (is_null($account_managers_value))
{
$account_managers_value = DB::table('users')
->orderBy(DB::raw('CONCAT(first_name," ",last_name)'))
->select(DB::raw('CONCAT(first_name," ",last_name) as amname'))
->lists('amname');
}
if (is_null($account_types_value))
{
$account_types_value = DB::table('account_types')
->orderBy('type')
->lists('type');
}
// In the view, there is a dropdown box, that allows the user to select the amount of records to show per page. Retrieve that value or set a default.
$perPage = Input::get('perPage', 10);
// This code retrieves the order from the session that has been selected by the user by clicking on a table column title. The value is placed in the session via the getOrder() method and is used later in the Eloquent query and joins.
$order = Session::get('account.order', 'company_name.asc');
$order = explode('.', $order);
// Here we perform the joins required and order the records, then select everything from accounts and select their id's as aid. Then whereIn is used to select records where company name, account manager name and account type matches the values of the multiselect boxes or the default set above.
$accounts_query = Account::leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')));
if (!empty($company_names_value)) $accounts_query = $accounts_query->whereIn('accounts.company_name', $company_names_value);
$accounts = $accounts_query->whereIn(DB::raw('CONCAT(users.first_name," ",users.last_name)'), $account_managers_value)
->whereIn('account_types.type', $account_types_value)
->paginate($perPage)->appends(array('company_names_value' => Input::get('company_names_value'), 'account_managers_value' => Input::get('account_managers_value'), 'account_types_value' => Input::get('account_types_value')));
$accounts_trash = Account::onlyTrashed()
->leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')))
->get();
$message = Session::get('message');
$default = ($perPage === null ? 10 : $perPage);
$this->layout->content = View::make('admin.accounts.index', array(
'accounts' => $accounts,
'accounts_trash' => $accounts_trash,
'company_names' => DB::table('accounts')->orderBy('company_name')->lists('company_name', 'company_name'),
'account_managers' => DB::table('users')->orderBy(DB::raw('CONCAT(first_name," ",last_name)'))->select(DB::raw('CONCAT(first_name," ",last_name) as amname'))->lists('amname', 'amname'),
'account_types' => DB::table('account_types')->orderBy('type')->lists('type', 'type'),
'perPage' => $perPage,
'message' => $message,
'default' => $default
));
}
Basically, I am building a query that searches several tables (hence the joins). In the view a user has the ability to select multiple values from various multi-select boxes and then submit a form which will then populate the $company_names_value, $account_managers_value and $account_types_value variables.
Initially, when there is no form submission, I'm using Query Builder to select all records for each type, and then using them in the query.
It works but it is slow and messy. I was wondering if any of you Laravel 4 gurus could help me improve it further, so that the queries are faster and the code is lighter.
Thanks in advance.

This has now been refactored significantly, and it is very fast now. I've moved most of my code in to my models, as well as refactoring that code.
Here's the new index method:
public function index()
{
$account = explode(',', Input::get('account'));
$account_manager = explode(',', Input::get('account_manager'));
$account_type = explode(',', Input::get('account_type'));
$perPage = Input::get('perPage', 10);
$order = Session::get('account.order', 'company_name.asc');
$order = explode('.', $order);
$accounts = Account::accounts($order, $account, $account_manager, $account_type)->paginate($perPage)->appends(array(
'account' => Input::get('account'),
'account_manager' => Input::get('account_manager'),
'account_type' => Input::get('account_type'),
'perPage' => Input::get('perPage')
));
$accounts_trash = Account::accountsTrash($order)->get();
$message = Session::get('message');
$default = ($perPage === null ? 10 : $perPage);
$this->layout->content = View::make('admin.accounts.index', compact('accounts', 'accounts_trash', 'message', 'default'));
}
And the new getAccountByName() method in my controller that is used during my AJAX call. This should probably go in the model:
public function getAccountByName()
{
$name = Input::get('account');
return Account::select(array('id', DB::raw('company_name as text')))->where('company_name', 'like', "%$name%")->get();
}
And finally two new methods in my model for retrieving accounts and accounts trash:
public function scopeAccounts($query, $order, $account, $account_manager, $account_type)
{
$query->leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')));
if (!empty($account[0])) {
$query = $query->whereIn('accounts.id', $account);
}
if (!empty($account_manager[0])) {
$query = $query->whereIn('users.id', $account_manager);
}
if (!empty($account_type[0])) {
$query = $query->whereIn('account_types.id', $account_type);
}
}
public function scopeAccountsTrash($query, $order)
{
$query->onlyTrashed()
->leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')));
}
Again, there's probably a ton of things to get tidied up here but I'm certainly closer to a much faster and cleaner solution. Doing it like this has reduced the loading times from 12 seconds to 234ms.

Related

Is it possible to combine whereHas with 'or' queries?

I'm trying to implement a filter system where, among other attributes and relationships, items are categorized. However, the challenge appears when combining OR queries with other filters using the regular and clause. The result grabs rows which I do not want and adds the or when the condition for that fails, thus polluting the final results with unwanted data.
<?php
class ProductSearch {
public $builder;
private $smartBuild; // this is the property I'm using to disable the alternation when other search parameters are present to avoid polluting their result
function __construct( Builder $builder) {
$this->builder = $builder;
}
public function applyFilterToQuery(array $filters) {
$pollutants = ['subcategory', 'subcategory2', 'category'];
$this->smartBuild = empty(array_diff( array_keys($filters), $pollutants)); // [ui=>9, mm=>4], [mm]
foreach ($filters as $filterName => $value) {
// dd($filters, $filterName );
if (method_exists($this, $filterName) && !empty($value) )
$this->$filterName( $value);
}
return $this;
}
public function location( $value) {
$this->builder = $this->builder
->whereHas('store2', function($store) use ($value) {
$store->where('state', $value);
});
}
public function subcategory( $value) {
$name = Subcategories::where('id', $value)->pluck('name');
$this->builder = $this->builder->where('subcat_id', $value);
if ($name->isNotEmpty() && $this->smartBuild) {
$names = preg_split('/\W\s+/', $name[0]);
if (!$names) $names = $name;
foreach ($names as $value)
$this->builder = $this->builder->orWhere('name', 'like', "%$value%");
}
}
}
You may observe from the above that making a request for categories searches products matching the category name. But on attempting to combine that alternate match with legitimate AND queries (in location for instance, the result tends to include matching locations OR matching names.
The desired result is ((matching name OR matching category) AND matching location). Is this possible?
I had similar situation like this few days ago about User and Posts.
Needed a list of posts which user has commented or participated in and which user owns.
So I did following on User model
//Get user created post or if user has participated in the post
$queryString->where(function ($query) use ($user_id) {
return $query->whereHas('participants', function ($q) use ($user_id) {
$q->where('user_id', $user_id);
})->orWhere('id', $user_id);
});
Hope this helps.

How to combine four queries in laravel?

I have draws on my site, in order to take part in the draw, you need to do a certain action per day. And there is a code that checks it all:
$date = Carbon::today();
$sta = \DB::table('ets')->where('user_id', $this->user->id)->where('created_at', '>=', $date)->get();
$sta = \DB::table('ets_1x1')->where('user_id', $this->user->id)->where('created_at', '>=', $date)->get();
$sta = \DB::table('ets_low')->where('user_id', $this->user->id)->where('created_at', '>=',$date)->get();
$sta = \DB::table('ets_duel')->where('user_id', $this->user->id)->where('created_at', '>=', $date)->get();
if ($sta == NULL) {
return response()->json(['status' => 'error', 'msg' => 'Error']);
}
This code checks if there is a user record in 4 possible tables. I made an entry in the table ets_1x1, but still I can’t take part, because the error seemed to not find me in the database. I removed all the tables and left only ets_1x1 and I was accepted into the drawing.
As I understand it, the value is taken from the last request. How can I combine a query into 1 and do a check on these 4 tables?
UPD:
I also tried to give new names to the variables and display the response code differently, now participation in the drawing is accepted from all people, even from those who have not fulfilled the conditions, now it looks:
if(!empty($sta_1) || !empty($sta_2) || !empty($sta_3) || !empty($sta_4)) {
return response()->json(['status' => 'error', 'msg' => 'Error']);
}
Where my mistake?
That code is not going to work because:
The first piece of code will evaluate only the last request (and in consecuence, only if there is any existent user on the last table only).
The second piece of code is not being evaluated correctly, you are running empty function on a Laravel collection.
Why don't you try this? I think it should work:
$date = Carbon::now();
$userExists = false;
$tables = ['ets', 'ets_1x1', 'ets_low', 'ets_duel'];
foreach ($tables as $tableName) {
$result = \DB::table($tableName)
->where('user_id', $this->user->id)
->where('created_at', '>=', $date)
->get()
;
if ($result->isNotEmpty()) {
$userExists = true;
break;
}
}
if (!$userExists) {
return response()->json(['status' => 'error', 'msg' => 'Error']);
}

How do I use a custom query in the model yii2 framework

How do I use a custom query in a model using the yii2 framework? I am trying but errors occurs.
I am a beginner in Yii2 Framework.
Here is my login model:
public function getUser($id)
{
$return = User::findBySQL("Select * from User where userType = 'AHP' and userid = '$id';");
return($return['AHPName']);
}
findOne()
Returns a single active record model instance by a primary key or an
array of column values.
$data = User::findOne(['userType' => 'AHP', 'userid' => $id]);
This will find the user whose user type is AHP and whose userid is $id.
public function getUser($id)
{
$data = User::findOne(['userType' => 'AHP', 'userid' => $id]);
return $data['AHPName'];
}
Try with ActiveQuery:
public function getUser($id)
{
$data = User::find()->where(['userType' => 'AHP', 'userid' => $id])->one();
return $data['AHPName'];
}
using custom queries:
public function getUser($id)
{
$sql = "Select * from User where userType = 'AHP' and userid = '.$id.'";
$return = User::findBySQL($sql)->one();
return $return->AHPName;
}
you can try this technique for writing a custom query in yii2
<?php
use yii\db\Query;
$query = new Query();
$query->select(['*'])->from('user')
->where('user.userType=:id',['id'=>'AHP'])
->andWhere('user.userid=:no',['no'=>$id]);
$command = $query->createCommand();
$result = $command->queryAll();
?>
another to do this
User::find()->where('userType = :type and userid = :id',
['type'=>AHP, 'id' => $id])->one();

Add items to query result - Laravel

I'm slowly moving my API to Laravel and coming to grips with the Query Builder.
I'm trying to achieve this:
$data = array();
$query = "SELECT * FROM blog_posts WHERE post_type = 3 AND post_status = 1 ORDER BY id DESC";
$result = mysqli_query($cms_connection, $query);
if($result) {
while($row = mysqli_fetch_assoc($result)) {
$row['post_seo'] = seoUrl($row['post_title']);
$data['data'][] = $row;
}
$data['success'] = true;
$response = json_encode($data);
}
My problem isn't necessarily with getting the query, but as you can see I'm using the result of the query and then injecting it back into the final array.
So essentially, I'm fetching rows, transforming some of the attributes fetched, and then injecting the newly created attributes into the resulting array.
This is what I have so far:
$posts = DB::table('blog_posts')
-where(['post_type' => 1, 'post_status' => 1)
->orderBy('id', 'desc')
->take(5)->get();
You could do it this way
// get your data (yours part of code)
$posts = DB::table('blog_posts')
-where(['post_type' => 1, 'post_status' => 1])
->orderBy('id', 'desc')
->take(5)->get();
// add post_seo
foreach ($posts as $post) {
$post->post_seo = seoUrl($post->post_title);
}
// set result array
$data['data'] = $posts;
$data['success'] = true;
// response
$response = response()->json($data);
// or in case you want to return it just
return response()->json($data);
EDIT
You could do it also a bit better, using Eloquent. If you have such model (you need to add valid namespaces and use statements)
class BlogModel extends Model
{
protected $table = 'blog_posts';
protected $appends = ['post_seo'];
public function getPostSeoAttribute($value)
{
return seoUrl($this->post_title);
}
}
(added accessor to post_seo attribute and added post_seo to results when converting to array)
You can now do (shorter syntax than in previous example):
// get your data
$posts = BlogPost::where('post_type',1)
->where('post_status',1)
->orderBy('id', 'desc')
->take(5)->get();
// response
$response = response()->json(['data' => $posts, 'success' => true]);

JOIN works in SQL not in Drupal

I'm trying to make a module for Drupal 7.x. At a certain point I want to use a sql query (JOIN). When I try the query in MYSQL it works. But when I want to try it in Drupal, the array is empty.
So I guess there is a difference between the sql query and the drupal query (mayby the implemantion is different).
SQL Query
SELECT * FROM friends
INNER JOIN users
ON friends.uid=users.uid
Drupal implementation
function project_myfriends(){
// Use database API to retrieve tasks
$query = db_select('friends', 'f');
$query->join('users', 'u', 'f.uid = u.uid'); // JOIN
$query->fields('u', array('name'))
->execute();
return $query;
}
/**
* Implements hook_block_view().
*/
function project_block_view($delta = ''){
switch ($delta) {
case 'project':
$block['subject'] = t('My Friends');
// Use our custom function to retrieve data
$result = project_myfriends();
$items = array();
var_dump($result);
foreach($result as $friend){
$items[] = array(
'data' => $friend->name,
);
}
// No tasks
if (empty($items)) {
$block['content'] = t('No friends.');
}
else {
// Pass data trough theme function
$block['content'] = theme('item_list', array(
'items' => $items));
}
}
return $block;
}
Thx in advance
You forgot to fetch your result query:
$result = project_myfriends()->execute()->fetchAll();
var_dump($result);