Yii2-activedataprovider doesn't fetch data - php

I use some $query->andFilterWhere(...) to create my query.
and can see the final query by echo $query->createCommand()->rawSql;
when I copy the final query and past it on phpmyadmin, 2 record fetched but no result found in ActiveDataProvider.
Where is the point that I miss that?!
============================================
This is my code:
$query = Camera::find();
$post = Yii::$app->request->post();
$post2 = array_filter((array)$post);
if( count($post2) >0 ){
foreach($post2 as $k=>$v){
$query->andFilterWhere([ 'Like' , $k , $v ]);
}
}
if($post['State'] > 0){
$branches = Branch::find()->joinWith('city')->where('state_id='.((int)$post['State']))->all();
foreach( $branches as &$v){
$v = $v->brch_id;
}
$query->andFilterWhere([ 'IN' , 'brch_id' , $branches ]);
}
echo $query->createCommand()->rawSql;
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);

The problem was this loop:
foreach( $branches as &$v){
$v = $v->brch_id;
}
I just replace it by:
$a = [];
foreach( $branches as $v){
$a[] = (int)$v->brch_id;
}
and DONE, Solved!!!!! :|

In your code you have
if( count($post2) >0 ){ // that means all fields filled
foreach($post2 as $k=>$v){
$query->andFilterWhere([ 'Like' , $k , $v ]);
}
}
And just after that you have a check for $post['State'] and you are using it for a joinWith. I Don't know what kinda search you are using (or what form did you build), but it seems you are searching for State in this both models... is that the correct behavior?
If that's correct, can you show us the raw sql query that worked for you, but not with ActiveDataProvider?
And can i ask why don't you use a class for this search and extends it from Camera?
It would be something similar to this:
public $state // fields that you Camera model don't have.
public function search($params){
$query = Camera::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andFilterWhere('like', 'attribute', $this->attribute);
// same for the others attributes here...
$query->joinWith(['nameOfRelationWithBranch' => function ($queryBranch) {
$queryBranch->joinWith(['city' => function ($queryCity) {
$queryCity->andFilterWhere('state_id', $this->state);
}]);
}]);
//echo $query->createCommand()->rawSql;
return $dataProvider;
}

Related

How to loop foreach in laravel dynamically

Am just learning Laravel and I have this logic were in I want to display array of total items based from user, to explain this further here is my database
user table
items table
this is my current code
public function display()
{
$users = User::where('type', 'Shop')->get();
foreach($users as $user){
$shop_id = $user['id'];
$shop_name = $user['name'];
}
$total = Item::where('user_id', $shop_id)->sum('total');
$shops =[
['Name' => $shop_name, 'total' => $total],
];
return response()->json([
"shops" =>$shops
], 200);
}
and here is my sample output:
am only getting 1 object instead of 2 as I have two shops how to loop this dynamically.
thanks
the $shops and $total variable is not in foreach loop that's because it returns only one row. and you must use $shops[] .
public function display()
{
$users = User::where('type', 'Shop')->get();
foreach($users as $user){
$shop_id = $user['id'];
$shop_name = $user['name'];
$total = Item::where('user_id', $shop_id)->sum('total');
$shops[] =['Name' => $shop_name, 'total' => $total];
}
return response()->json([
"shops" =>$shops
], 200);
}
but the best and clean way is to use laravel relationship
in User model:
public function items()
{
return $this->hasMany(Item::class) ;
}
and display controller :
public function display()
{
$shops = User::where('type', 'Shop')->get()
->mapWithKeys(function($user){
return ['name'=>$user->name ,
'total'=> $user->items->sum('total')
]});
return response()->json(["shops" =>$shops], 200);
}
Do this
$shops[] = ['Name' => $shop_name, 'total' => $total];
to push all the shops into one array.
You are currently overriding the hole array.
UPDATE: Also move the sql part into the foreach:
foreach($users as $user){
$shop_id = $user['id'];
$shop_name = $user['name'];
$total = Item::where('user_id', $shop_id)->sum('total');
$shops[] =['Name' => $shop_name, 'total' => $total];
}

creating multiple rows based on record in an array - laravel

I have the following query that brings back an array of user_ids, where each user will get notified.
$pstusrmembs = DB::table('postusrs')
->where('post_id', $post_id)
->where('accepted', 1)
->pluck('user_id');
I need to put each user_id in a different row in the below "insert" code :
$notifs = new Notif();
$notifs->rec_uid = $pstusrmembs;
$notifs->title = $post_title;
$notifs->save();
How can I change $notifs->rec_uid = $pstusrmembs; to do this ?
Use for loop for this:
foreach($pstusrmembs as $userID){
$notifs = new Notif();
$notifs->rec_uid = $userID;
$notifs->title = $post_title;
$notifs->save();
}
And If You are using the Mass Assignment concept
foreach ($pstusrmembs as $userID) {
Notif::create(['rec_uid' => $userID, 'title' => $post_title]);
}
And If You are using the Mass Assignment concept Without Any Model
Events or Listeners
foreach ($pstusrmembs as $userID) {
$arrayOfNotif[] = ['rec_uid' => $userID, 'title' => $post_title];
}
Notif::insert($arrayOfNotif);
I will recommend to use DB transaction to keep a database consistent even in cases of system failure and prepare that list as array and insert in one line ....
$prepare = [];
foreach($pstusrmembs as $p) {
$prepare[] = [
'rec_id' => $p,
'title' => $post_title
];
}
DB::beginTransaction();
try {
Notif::insert($prepare);
DB::commit();
} catch (Exception $e) {
DB::rollback();
}

Laravel Increase SQL speed

I am trying to increase the speed of my queries in Laravel 5.7 and I have the call down to ~2.5 seconds. I am trying to figure out more ways to make it faster and if I could get some help I'd greatly appreciate it.
Thanks
How my data is structured:
Function(Controller):
public function getUserDataTmp(Request $request) {
$input = file_get_contents("php://input");
$request = json_decode($input);
if ($this->authTokenAccess($request) == true) {
$bottomWords = bottom_exterior_word::select('word','sentence','sequence','id','group_id')->where('user_id','=', $request->id)->get();
$emergencyWords = left_exterior_word::select('word','sentence','sequence','id')->where('user_id','=', $request->id)->get();
foreach($bottomWords as $tmp => $key) {
$group_id = $key->group_id;
$bottomWords->user_id = $request->id;
$bottomWords[$tmp]->words = $key->getMainWords($group_id, $request->id);
}
foreach($emergencyWords as $key => $word) {
$emergencyWords[$key]->image = imageModel::select('base64','id')->where('emergency_id','=', $word->id)->first();
}
$data = [
'data' => [
'return' => 'success',
'code' => 'VEDC001',
'response' => 'Successfully Gathered Words',
'main_categories' => $bottomWords,
'emergency_words' => $emergencyWords
]
];
return(json_encode($data));
}
}
getMainWords Function(bottom_exterior_word model):
public function getMainWords($group_id, $id)
{
// return("TEST");
$words = \App\main_word::select('id','group_id','sentence','sequence','word')->where('group_id','=', $group_id)->where('user_id','=', $id)->get();
foreach ($words as $key => $word) {
$words[$key]->image = Image::select('base64','id')->where('word_id','=', $word->id)->first();
}
return $words;
}
Start by refactoring so that you dont query inside a foreach loop
foreach($bottomWords as $tmp => $key) {
$group_id = $key->group_id;
$bottomWords->user_id = $request->id;
$bottomWords[$tmp]->words = $key->getMainWords($group_id, $request->id);
}
I would change the getMainWords function to accepts an array of group id's and use the whereIn clause:
The whereIn method verifies that a given column's value is contained
within the given array:
$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();
Same treatment for this loop.
foreach($emergencyWords as $key => $word) {
$emergencyWords[$key]->image = imageModel::select('base64','id')->where('emergency_id','=', $word->id)->first();
}
In general minimizing the NUMBER of queries will improve response time.
Old post, would just like to update it though. Since I have first posted this, I have learned a lot more about Laravel and am a lot more experienced with it.
Here is my new function and solution:
Controller:
public function data(Request $request)
{
return response()->success(
[
'emergencywords' => EmergencyWord::with('image')->whereUserId($request->user()->id)->get(),
'categorywords' => CategoryWord::with(['image','words.image'])->whereUserId($request->user()->id)->get(),
]
);
}
Category Word Relationships:
public function image()
{
return $this->hasOne('App\Image','id','image_id');
}
public function words()
{
return $this->hasMany('App\MainWord','category_words_id','sequence');
}
Emergency Word Relationships:
public function image()
{
return $this->hasOne('App\Image','id','image_id');
}
Main Word Relationships:
public function image()
{
return $this->hasOne('App\Image','id','image_id');
}

Laravel collection map function overrides default collection

I have a code where I use map to create a new collection of high scores.
The problem I have is that it overrides the default user collections. Which is not my intention.
Here is the code
$users = Users::all();
$highscore = $users->map(
function ($user) {
$calls = $user->calls->filter(
function ($call) {
$date = Carbon::parse($call->datetime)->format("Y-m-d");
$today = Carbon::now()->format("Y-m-d");
return $date == $today;
}
);
return [
'id' => $user->id,
'duration' => $calls->sum('duration'),
];
}
);
If i dump the first user after getting all the users I get the first user. Like this.
$users = Users::all();
dd($users->first());
If I dump the first user after the high score map. I get all Calls from that user which is another model. Which means that the users collection has been modified. Like this.
$highscore = $users->map(
function ($user) {
$calls = $user->calls->filter(
function ($call) {
$date = Carbon::parse($call->datetime)->format("Y-m-d");
$today = Carbon::now()->format("Y-m-d");
return $date == $today;
}
);
return [
'id' => $user->id,
'duration' => $calls->sum('duration'),
];
}
);
dd($users->first()):
Any idea on how to handle this behaviour?
The map function returns an array of [[$userId => $duration], ...]. What you want to do is to order your users by the sum of their calls.
I believe that, in order to do that easily, you should add to your User model:
public function getTodayCallSum() {
return $user->calls->filter(function($call) {
$date = Carbon::parse($call->datetime)->format("Y-m-d");
$today = Carbon::now()->format("Y-m-d");
return $date == $today;
})->sum('duration');
}
And then edit your query:
$users = User::all();
$firstUser = $users->sortBy('todayCallSum')->first();
I haven't tested this code, but I think it should help you towards the right direction.

How can I pass parameter/dropdown value to model function

I am using yii2 advanced template improved
Not sure if relevant to problem/a solution but to get to the category I have a form with some javascript which on change redirects the user to the category selected.
I have the following code which allows me to access the posts within that category id I go to localhost:8888/advanced/article/category?id=1
The problem at the minute is that if I call getCategoryName function in my model from my category view the id parameter isn't being passed to the model function therefore when using getCategoryName defaults to sport.
public function actionCategory($id)
{
$model = new Article();
$searchModel = new ArticleSearch();
$query = Article::find()
->where(['category' => $id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
return $this->render('category', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'model'=>$model,
]);
}
Then within my view I use the following which works to an extent in terms of executing the model function, however I am unsure on how to pass the parameter/current category id to the model function. The below code work for the _index & in the single article view.
<?= $model->CategoryName ?>
This is my model function
public function getCategoryName($category = null)
{
$category = (empty($category)) ? $this->category : $category ;
if ($category === self::CATEGORY_ECONOMY)
{
return Yii::t('app', 'Economy');
}
elseif ($category === self::CATEGORY_SOCIETY)
{
return Yii::t('app', 'Society');
}
else
{
return Yii::t('app', 'Sport');
}
}
Use something like this, in your view
$request = Yii::$app->request;
$id = $request->get('id');//get id from request or change according to your requirement
echo $model->getCategoryName($id);//full method name here
Try to use this. change === to ==:
public function getCategoryName($category = null)
{
$category = (empty($category)) ? $this->category : $category ;
if ($category == self::CATEGORY_ECONOMY)
{
return Yii::t('app', 'Economy');
}
elseif ($category == self::CATEGORY_SOCIETY)
{
return Yii::t('app', 'Society');
}
else
{
return Yii::t('app', 'Sport');
}
}

Categories