Laravel merge logic - laravel

In my case i need to use collections together. But also i need that model too. I decided to merge eloquents. Like this:
$products = Product::orderBy('created_at', 'desc')->get();
$sliders = Slider::orderBy('order', 'asc')->get();
$news = News::orderBy('order', 'asc')->get();
$collection = new ModelCollection();
$result = $collection->merge($products)->merge($news)->merge($sliders)->sortByDesc('created_at');
dd($result);
$products has 4 data, $slider also has 4 data, $news has 3 data.
But i am allways get 4 data by sorting created_at. Why i can't get 11 data from the collection?
https://laravel.com/docs/5.6/collections#method-merge
in this case there is no limit or rules.
in the interest of brevity i am trying to make an array with my selected models. If its a collection it keeps model. If its just an array also i need to add the model name for each data collections.
array(
Products > product1,product2,product3,product4
News > news1, news2, news3
Slider > slider1, slider2, slider3
)

If you want all collections in one array you can do by this,
$products = Product::orderBy('created_at', 'desc')->get();
$sliders = Slider::orderBy('order', 'asc')->get();
$news = News::orderBy('order', 'asc')->get();
$result = array();
$result['products'] = $products;
$result['sliders'] = $sliders;
$result['news'] = $news;
This will create collection $result like you want,
array(
products > product1,product2,product3,product4
news > news1, news2, news3
sliders > slider1, slider2, slider3
)
Hope this will help you. Comment if any doubts.

Related

Laravel 9 hasMany relationship not working

I have a model with a relationship with another model, when calling the relationship in the controller it gives me
Exception: Property [products] does not exist on the Eloquent builder instance.
model:
public function products(): HasMany
{
return $this->hasMany(CartProduct::class,'cart_id','id');
}
controller
public function showCartOfAuth()
{
$id =auth()->guard('customers')->user()->id;
$cart = Cart::where('customer_id',$id)->get();
$products = Cart::where('customer_id',$id)->products->get();
$response = [
'cart' => $cart,
'items' => $products
];
return response($response,200);
}
Can you try this, please: Cart::with('products')->where('customer_id',$id)->get();
The way you have implemented requires atleast two database queries.
One: $cart = Cart::where('customer_id',$id)->get();
Two: Cart::where('customer_id',$id)->first()->products; //maybe a third query to fetch the products
For better performance you should
public function showCartOfAuth()
{
$id =auth()->guard('customers')->user()->id;
// Either Option 1
// Possibly 2 database queries, one to fetch the Cart and another to fetch Products for the Cart
$cart = Cart::where('customer_id',$id)->first();
$products = $cart->products;
$response = [
'cart' => $cart,
'items' => $products
];
// OR Option 2
// One database query using eager loading
$cart = Cart::with('products')->where('customer_id', $id)->first();
$response = ['cart' => $cart];
return response($response,200);
}
If you use option 2 from above then instead of $items you can use $cart->products

Laravel query is not working with multiple IDs

I am trying to fetch multiple rows of selected IDs from db. Here is my controller code
$products = new product;
$products = $products->select('products.*', 'categories.category_name')->join('categories', 'products.product_category', '=', 'categories.id');
$products = $products->whereIn('products.id', [$request->list]); //$request->list is post value (12,13)
$products = $products->get();
Here $request->list is post value which contains 12,13. Mentioned code works fine if I manually type IDs like this.
$products = $products->whereIn('products.id', [12,13]);
But if I try to call same with variable or directly with request post then it is returning only one result.
$products = $products->whereIn('products.id', [$request->list]);
//OR
$id = $request->list;
$products = $products->whereIn('products.id', $id);
Why it is giving only one result when I use variable, any idea?
$request->list contains comma separated values, so convert it to an array. Change it as below:
$products = $products->whereIn('products.id', explode(',',$request->list));
Here is a full query:
$products = product::select('products.*', 'categories.category_name')
->join('categories', 'products.product_category', '=', 'categories.id')
->whereIn('products.id',explode(',',$request->list))
->get();

Laravel eloquent query loads hundreds of models

I'm querying a relationship with pagination, yet in my debugbar I can see that all models are loaded in memory and if I'm correct that should not be happening.
I have a Post model with a hasMany relationship to Comments. I have a few lines of code as below. They are written in this order because there are parameters in between that I need to apply. I have shown filterScore here but there are multiple that work the same way.
$post = Post::find(1);
$comments = $post->comments();
$comments = $comment->filterScore($comments)
$comments = $comments->orderBy('created_at', 'DESC');
return $comments->paginate(25);
private function filterScore($q)
{
if($this->score > 0)
return $q->where('score', $this->score);
return $q;
}
The raw query if $this->score = 0:
select * from `comments` where `comments`.`post_id` = 1 and `comments`.`post_id` is not null order by `created_at` desc limit 25 offset 0
UPDATE
I've tried writing it like this, based on this post, but then I still get the same result: all models are loaded into memory.
$post = Post::find(1);
$comments = Comment::query();
$comments = Comment::where('post_id', $post->id);
$comments = $comment->filterScore($comments);
return $comments->paginate(25);
In the Laravel debugbar you can see that all models are loaded into memory, instead of just 25:
One solution is, as #nikistag wrote in comment: $post->comments()->orderBy('created at' , 'DESC')->paginate(25); (you have to have all in one chained expression).
But if you use optional parameters, it can be difficult to achieve someting like that.
In this case, you can just change it to 2 seperate codes, where you do not use laravel relationship:
$post = Post::find(1);
$comments = Comment::where('post_id', $post->id);
if(!empty($from)) //if set optional parameter, add condition
$comments->where('created_at', '>=', $from);
$comments->orderBy('created_at', 'desc')->paginate(25);

how to group by item many to many query builder laravel

I have comics and categories related many to many through pivot tables. I use join and groupBy caterories.id but it doesn't group categories same comics id.
If I do not use the group
$result = DB::table('comics')
->select('comics.name','categories.title')
->join('category_comic', 'comics.id', '=', 'category_comic.comic_id')
->join('categories', 'category_comic.category_id', '=', 'categories.id')
->get();
I want each comic group category
(source: uphinh.org)
First of all
try tou use Laravel Eloquent Relationship and its really cool and very easy to use
In Eloquent Method
In Comics Model
add relationship function
public function cat(){
return $this->hasOne(ComicCategory::class,'id','comic_id'); //ComicCategory::class is your category_comic model
}
Then you just need you Comic Model to get a group of your category com
$result = Comic::with('cat')->get(); //Commic is your model
In query Builder, you need to group manual using foreach
$result = DB::table('comics')
->select('comics.name','categories.title')
->join('category_comic', 'comics.id', '=', 'category_comic.comic_id')
->join('categories', 'category_comic.category_id', '=', 'categories.id')
->get();
$result = json_decode(json_encode($result), TRUE);
$new_result = [];
foreach($result as $row){
$arr[$row['id']]['name'] = $row['name'];
$arr[$row['id']]['cat'][] = $row['title'];
}
and finally rearange the key
$new_result = array_values($new_result );
PS : thats array method, i never group using object method

Laravel how do I get the row number of an object using Eloquent?

I'd like to know the position of a user based on its creation date. How do I do that using Eloquent?
I'd like to be able to do something like this:
User::getRowNumber($user_obj);
I suppose you want MySQL solution, so you can do this:
DB::statement(DB::raw('set #row:=0'));
User::selectRaw('*, #row:=#row+1 as row')->get();
// returns all users with ordinal 'row'
So you could implement something like this:
public function scopeWithRowNumber($query, $column = 'created_at', $order = 'asc')
{
DB::statement(DB::raw('set #row=0'));
$sub = static::selectRaw('*, #row:=#row+1 as row')
->orderBy($column, $order)->toSql();
$query->remember(1)->from(DB::raw("({$sub}) as sub"));
}
public function getRowNumber($column = 'created_at', $order = 'asc')
{
$order = ($order == 'asc') ? 'asc' : 'desc';
$key = "userRow.{$this->id}.{$column}.{$order}";
if (Cache::get($key)) return Cache::get($key);
$row = $this->withRowNumber($column, $order)
->where($column, '<=',$this->$column)
->whereId($this->id)->pluck('row');
Cache::put($key, $row);
return $row;
}
This needs to select all the rows from the table till the one you are looking for is found, then selects only that particular row number.
It will let you do this:
$user = User::find(15);
$user->getRowNumber(); // as default ordered by created_at ascending
$user->getRowNumber('username'); // check order for another column
$user->getRowNumber('updated_at', 'desc'); // different combination of column and order
// and utilizing the scope:
User::withRowNumber()->take(20)->get(); // returns collection with additional property 'row' for each user
As this scope requires raw statement setting #row to 0 everytime, we use caching for 1 minute to avoid unnecessary queries.
$query = \DB::table(\DB::raw('Products, (SELECT #row := 0) r'));
$query = $query->select(
\DB::raw('#row := #row + 1 AS SrNo'),
'ProductID',
'ProductName',
'Description',
\DB::raw('IFNULL(ProductImage,"") AS ProductImage')
);
// where clauses
if(...){
$query = $query->where('ProductID', ...));
}
// orderby clauses
// ...
// $query = $query->orderBy('..','DESC');
// count clause
$TotalRecordCount = $query->count();
$results = $query
->take(...)
->skip(...)
->get();
I believe you could use Raw Expresssions to achieve this:
$users = DB::table('users')
->select(DB::raw('ROW_NUMBER() OVER(ORDER BY ID DESC) AS Row, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
However, looking trough the source code looks like you could achieve the same when using SQLServer and offset. The sources indicates that if you something like the following:
$users = DB::table('users')->skip(10)->take(5)->get();
The generated SQL query will include the row_number over statement.
[For Postgres]
In your model
public function scopeWithRowNumber($query, $column = 'id', $order = 'asc'){
$sub = static::selectRaw('*, row_number() OVER () as row_number')
->orderBy($column, $order)
->toSql();
$query->from(DB::raw("({$sub}) as sub"));
}
In your controller
$user = User::withRowNumber()->get();

Resources