I am playing with Laravel models and I need one to return a value that is not in the db table but it comes by running a model method. This method runs a query that groups and count grouped results.
The model method works just fine but I don't seem to be able to pre-fill the $quantity variable within the constructor with something different than 0.
So this is an excerpt of the model:
public $quantity;
function __construct($attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->quantity = $this->quantity();
}
public function quantity()
{
$query = DB::table('carts_shopping')
->select('cart_id', DB::raw('COUNT(*) AS quantity'))
->where('cart_id',$this->cart_id)
->groupBy('cart_id')
->first();
return ($query) ? $query->quantity : 0;
}
While this is how I am trying to retrieve the results from controller:
$cartitems = Auth::user()->cartshopping;
foreach ($cartitems as $cartitem)
{
echo $cartitem->name;
echo $cartitem->quantity;
}
As you may guess 'cartshopping' comes from the user model being related with the model excerpt I pasted.
I also noticed that quantity() method gets called and it returns 0 all the time as if $this->cart_id was empty and, changing $this-cart_id with a real value the query itself doesn't even get executed.
Thanks a lot for any suggestion you guys can share.
Have you tried accessing the properties using $this->attributes?
public $quantity;
function __construct($attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->quantity = $this->quantity();
}
public function quantity() {
$query = DB::table('carts_shopping')
->select('cart_id', DB::raw('COUNT(*) AS quantity'))
->where('cart_id', $this->attributes['cart_id'])
->groupBy('cart_id')
->first();
return ($query) ? $query->quantity : 0;
}
Failing that, you could try using the Eloquent accessors, which would be the best way to do it. This would make it dynamic as well, which could be useful.
class YourModel {
// Normal model data here
public function getQuantityAttribute() {
$query = DB::table('carts_shopping')
->select('cart_id', DB::raw('COUNT(*) AS quantity'))
->where('cart_id', $this->attributes['cart_id'])
->groupBy('cart_id')
->first();
return ($query) ? $query->quantity : 0;
}
}
Related
i am using laravel 7 eloquent. i am very new to laravel
this is basic method i am getting data from category table
public function category(){
$category = Category::orderBy('id', 'desc')->get();
$data['pageData'] = $category;
$data['pageTitle'] = "Category";
return view('admin.category.index')->with('data',$data);
}
But i want to do something like this
public function category(){
$category = Category::orderBy('id', 'desc')->get();
if(!empty($_REQUEST['parent_id']))
$category->where('parent_id',$_REQUEST['parent_id']);
$data['pageData'] = $category;
$data['pageTitle'] = "Category";
return view('admin.category.index')->with('data',$data);
}
So how can this is possible using eloquent.
Any help out of this question are helpful for me as i am beginner.
Try this
public function category(){
$query = Category::orderBy('id','desc');
$query = where('parent_id',$_REQUEST['parent_id']);
$category = $query->get();
$data['pageData'] = $category;
$data['pageTitle'] = 'Category';
return view('admin.category.index')->with('data',$data);
}
You can leverage the when() eloquent method to add conditional clause to query:
public function category()
{
$category = Category::when(request('parent_id'),function($parent_id,$query)
{
$query->where('parent_id',$parent_id);
})->orderBy('id', 'desc')->get();
$data['pageData'] = $category;
$data['pageTitle'] = "Category";
return view('admin.category.index')->with('data',$data);
}
when() doc reference https://laravel.com/docs/8.x/queries#conditional-clauses
You can use the when method to only apply the query if the request contains the parent_id.
From the docs:
Sometimes you may want certain query clauses to apply to a query based
on another condition. For instance, you may only want to apply a where
statement if a given input value is present on the incoming HTTP
request. You may accomplish this using the when method:
$role = $request->input('role');
$users = DB::table('users')
->when($role, function ($query, $role) {
return $query->where('role_id', $role);
})
->get();
The when method only executes the given closure when the first
argument is true. If the first argument is false, the closure will not
be executed. So, in the example above, the closure given to the when
method will only be invoked if the role field is present on the
incoming request and evaluates to true.
In your case it would be the following:
use Illuminate\Http\Request;
public function category(Request $request)
{
$categories = Category::query()
->when($request->input('parent_id'), function ($query, $parent_id) {
$query->where('parent_id', $parent_id);
})
->orderBy('id', 'desc')
->get();
$data['pageData'] = $categories;
$data['pageTitle'] = "Category";
return view('admin.category.index')->with('data', $data);
}
You can do as like below
public function category(){
$category = new Category();
if(!empty($_REQUEST['parent_id'])){
$category->where('parent_id',$_REQUEST['parent_id']);
}
$category = $category->orderBy('id', 'desc')->get()
$data['pageData'] = $category;
$data['pageTitle'] = "Category";
return view('admin.category.index')->with('data',$data);
}
Try using laravel when condition
https://laravel.com/docs/8.x/queries#conditional-clauses
Improve your question with proper details. as you mention in the question the second coding path won't work because it doesn't have $request query to functionally run the if statement. anyhow if you want to get only pageData & pageTitle in laravel Eloquent ORM you have to select both columns as follows.
class CategoryController extends Controller
{
public function category(){
$category = Category::select('pageData', 'pageTitle')->orderBy('id', 'desc')->get();
return view('admin.category.index')->with('data', $category);
}
}
I've written the following function in my Car model that does the following:
Gets the related reservations based on two dates(pickup/dropoff)
Checks if the amount of these reservations are equal or exceed the quantity of the car
Finally returns a boolean depending on the output
/**
* Custom Functions
*/
public function isAvailableFor($from, $to) {
$reservationsCount = $this->reservations->where('pickup_date', '>=', $from)->where('dropoff_date', '<=', $to)->count();
if($reservationsCount >= $this->quantity) {
return false;
}
return true;
}
The function is working as expected but I want to implement this in a more elegant way using local scopes so I can actually use it efficiently when querying the Car model in my controllers but I can't find the correct way to do it and my code becomes a complete mess.
For example I have the following scope that I am using by just typing Car::active()->get();
/**
* Scopes
*/
public function scopeActive($query)
{
return $query->where('status', 'active');
}
The main problem is the count() function that doesn't let me implement my function in a scope-way or at least I am not that experienced to come up with a solution.
Thanks in advance.
Update
As correctly pointed by OsDev since my function returns a boolean it can not be implemented directly in the scope function. I can alternatively do this in my scope function but I guess it is pretty much an overkill:
public function scopeAvailable($query, $from, $to) {
$excludedId = array();
$cars = Car::whereHas('reservations')->get();
foreach($cars as $car) {
if(!$car->isAvailableFor($from, $to)) {
array_push($excludedId, $car->id);
}
}
return $query->whereNotIn('id', $excludedId);
}
You have to return the $query instead the count result because that way you don't break the Query Builder chain
You can't combine scopes and Model functions because scopes are supposed to return the $query builder object and in that example, your function is returning a boolean.
You can do something like this
/**
* Scopes
*/
public function scopeIsAvailableFor($query,$from,$to)
{
return $query->where('pickup_date', '>=', $from)->where('dropoff_date', '<=', $to);
}
Then you can chain it and call count if you want
$count = Car::active()->isAvailableFor('2020-05-03','2020-05-06')->count();
Maybe you can wrap your new scope into your model method
public function isAvailableFor($from, $to) {
$reservationsCount = $this->reservations->isAvailableFor($from,$to)->count();
return !$reservationsCount >= $this->quantity;
}
I am trying to retrieve the data on my wishlist table, for a particular user, so far it only retrieves the first data on the table, just returning one array instead of the three in the table with same user id
public function getWishlistByUserId($id){
$wishlists = Wishlist::where('userId', $id)->get();
foreach($wishlists as $wishlist){
$products = Product::where('id', $wishlist->productId)->get();
return $products;
}
}
It happens because the foreach loop returns a value during the first iteration. Place your return statement outside the loop. Also you could improve your performence by making use of relationships.
An example could be:
// Product.php
public function wishlists()
{
return $this->hasMany(Wishlist::class);
}
// Your method
public function getWishlistByUserId($id)
{
return Product::whereHas('wishlists', function ($query) use ($id) {
$query->where('userId', $id);
});
}
Ideally this is n+1 situation
So i will suggest to use laravel relationship like:
in your whishlist model
public function product(){
return $this->hasMany(Product::class,'productId','id');
}
get data with relationship
public function getWishlistByUserId($id){
$wishlists = Wishlist::with('product')->where('userId', $id)->get();
}
I was finally able to get it working this way, i just pushed the result into an array, and then returned it outside the loop, thanks everyone for your help
public function getWishlistByUserId($id){
$wishlists = Wishlist::where('userId', $id)->get();
$wishlist = [];
foreach($wishlists as $wish){
$product = Product::where('id', $wish->productId)->get();
array_push($wishlist, $product);
}
return $wishlist;
}
I've been trying to do this for a while now. Most of the time, I solve it by using accessors. I'm currently trying to get if the column exists and I created a function in my model which is suppose to return boolean.
Model code:
class Inventory extends Model
{
protected $attributes = ['inventory'];
protected $appends = ['colorThumb'];
public function hasAttribute($attr){
return array_key_exists($attr, $this->attributes);
}
}
Controller code:
public function allInvOperation(Request $request){
$inv = Inventory::where('is_deleted', 0)->with(['product', 'size','color'])->orderByDesc('id')->get();
if(!is_null($request->searchText)){
dd($inv->hasAttribute('inventory'));
$inv = Inventory::where('is_deleted', 0)->with(['product', 'size','color'])->orderByDesc('id');
if($request->inv_filter == 'inventory'){
$inv = $inv->where('inventory', 'like', "%".$request->searchText."%")->get();
}
if($request->inv_filter == 'code'){
$inv = $inv->whereHas('product', function ($q) use ($request){
$q->where('code', "%".$request->searchText."%");
})->get();
}
}
ERROR
Method Illuminate\Database\Eloquent\Collection::hasAttribute does not exist.
The code you are doing hasAttribute on is a Collection of objects, you need to use first on that query to get a single result on which you can later do hasAttribute
I Have this 3 tables like below :
Tools
Parts
Part_details
it is my the table structure :
Tool -> has many -> parts. part -> has many->part_details.
Tool : id*, name; Parts : id*, name, tool_id; part_details: id, part_id, total;
Question :
Using laravel Model, how can I get Tool with One part that has biggest total on parts_details ??
// Tool Model
public function parts(){
return $this->hasMany(Part::class);
}
// Part Model
public function part(){
return $this->belongsTo(Tool::class);
}
public function part_details(){
return $this->hasMany(PartDetail::class);
}
// PartDetail Model
public function part(){
return $this->belongsTo(Part::class);
}
Now query the Tool model
$tools = Tool::with('parts')->withCount('parts.part_details')->get();
$toolWithMaxCount = $tools->filter(function($tool) use ($tools){
return $tool->parts->max('par_details_count') === $tools->max('parts.part_details_count');
})->first();
You can improve this with adding some raw bindings to optimise it. I think you got the idea.
Tool model
public function parts() {
return $this->hasMany('App\Part');
}
Part Model
public function details() {
return $this->hasMany('App\PartDetail');
}
public function tool() {
return $this->belongsToMany('App\Tool');
}
Detail Model
public function part() {
return $this->belongsToMany('App\Part');
}
Controller
$tools = Tool::with('parts', 'parts.details')
->find($id)
->max('parts.part_details');
Use the the hasManyThrough Relationship to get the all part details related to tool and then you can check the one by one record and get the highest total of the tool part.
// Tool Model
public function partsdetails()
{
return $this->hasManyThrough('App\PartDetail', 'App\Part','tool_id','part_id');
}
In Your controller
$data = Tool::all();
$array = [];
if(isset($data) && !empty($data)) {
foreach ($data as $key => $value) {
$array[$value->id] = Tool::find($value->id)->partsdetails()->sum('total');
}
}
if(is_array($array) && !empty($array)) {
$maxs = array_keys($array, max($array));
print_r($maxs);
}
else{
echo "No Data Available";
}