Laravel eloquent, How to define relationships with versioning - php

I have a product model with versioned data and I got a category model that can have one or many products. When I want to delete a category the code has to check if there are products related to the category. How to define the relation so that it goes to the products_version table to check if the category_id exists here before deleting it?
Here is my product model:
use Versionable, SoftDeletes;
protected $fillable = ['product_name', 'supplier', 'unit', 'pieces', 'desired_stock', 'current_stock', 'category_id', 'price'];
public $timestamps = true;
public $versioned = ['category_id', 'product_name', 'supplier','unit','desired_stock','current_stock','price','pieces', 'deleted_at'];
public function parent()
{
return $this->belongsTo('App\Category');
}
Versioning for products is working. I used the following code for it: https://github.com/ProAI/eloquent-versioning
My category model:
public function products()
{
return $this->hasMany('App\Product');
}
The code I used to check if there are products related to the category:
public function destroy($id)
{
// delete
$category = Category::withCount('products')->where('id', '=', $id)->first();
if ($category->products_count > 0) {
Session::flash('message', 'Can not delete category');
} else {
$category->delete();
// redirect
Session::flash('success', 'Category deleted');
}
return Redirect::to('categories');
}
The error message I get:
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'products.category_id' in 'where clause' (SQL: select categories.,
(select count() from products inner join products_version on
products.id = products_version.ref_id and
products_version.version = products.latest_version where
categories.id = products.category_id and
products_version.deleted_at is null) as products_count from
categories where id = 1 limit 1)

You may need something like has many through relationship
public function products()
{
return $this->hasManyThrough('App\Product', 'App\ProductVersion');
}
So it will link Category to a Product through ProductVersion because 'category_id' exists only in ProductVersion
Check Laravel Docs for more info
P.S. You may need to create ProductVersion model for products_version table

This might work:
$category = Category::withCount('products' => function($q) {
$q->where('id', '=', $id);
})->first();

Related

I need to get a list of users with a sum of pending orders amount in Laravel 5.4

I have a Laravel 5.4 app I created a while back. I need to add a functionality:
I need to fetch a list of users with a sum of pending orders amount.
name |email |pending_amount
user1_name |user1_email |1249
user2_name |user1_email |23424
I have these models
User.php
...
public function orders()
{
return $this->hasMany(Order::class, 'writer_id', 'id');
}
...
Order.php
class Order extends Model
{
//
protected $fillable = [
'source_id',
'client_id',
'user_id',
'status_id', // unpaid, pending or completed
'title',
'description',
'amount',
];
This is what I have.
public function index()
{
//
$writers = User::whereHas("roles", function ($q) {
$q->where("name", "customer")
->orWhere("name", "customer");
})
->with("orders")
->get();
return response()->json([
'customers' => $customers
]);
The query above is providing all the orders that a user has, however I only need the total amount of pending orders from the orders table.
The end result should be something like this
Update
This is what I have on the fetch query
<?
$users = User::whereHas("roles", function ($q) {
$q->where("name", "user")
->orWhere("name", "users");
})
->whereHas("orders", function($q) use($completed, $approved) {
$q->where('status_id', $completed)
->orWhere('status_id', $approved)
->sum('amount');
})
->get();
But am getting this error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users.id' in 'where clause' (SQL: select sum(`amount`) as aggregate from `orders` where `users`.`id` = `orders`.`user_id` and `status_id` = 9 or `status_id` = 6)
Possible solution is the following
$users = whereHas("roles", function ($q) {
$q->where("name", "user")
->orWhere("name", "users");
})
->whereHas("orders", function($q) use($completed, $approved) {
$q->where('status_id', $completed)
->orWhere('status_id', $approved);
})
})->with('orders')->get();
And then make a calculated field in your user model class. like this
private $pendingAmount
/**
* #return mixed
*/
public function getPendingAmount()
{
return $this->pendingAmount;
}
/**
* #param mixed $pendingAmount
*/
public function setPendingAmount($pendingAmount)
{
$this->pendingAmount = $pendingAmount;
}
public function calcPendingAmount(){
$this->pendingAmount = $this->orders()->sum('amount');
}
Next, in the loop, calculate the sum for each user.
foreach($users as &$user){
$user->calcPendingAmount();
}
This field can be accessed using the get method
There is no function "withSum" in laravel 5.4,like the latest versions of Laravel. therefore, either an additional loop or not use eloquent, but use DB facade and query builder.
Database: Query Builder

I am trying to query two tables in my laravel database

I am trying to query two tables in a database but its returning this error.
I am trying to implement a search through multiple tables. The project is an online store with 3 distinctive tables, Products, Categories and Brands. I can only search through the Products table but can't seem to get the same search field from my blade file to search either the categories or the brands and return results of the associated products.
QLSTATE[23000]: Integrity constraint violation: 1052 Column 'status' in where clause is ambiguous (SQL: select * from `categories` inner join `products` on `products`.`category_id` = `categories`.`id` where `name` LIKE %Television% and `status` = 1)
My Search function
public function searchProducts(Request $request) {
$product = $request->input('product');
$categories = Category::with('categories')->where(['parent_id' => 0])->get();
$productsAll = Category::query()->join('products', 'products.category_id', '=', 'categories.id')
->where('name', 'LIKE', "%{$product}%")
->where('status', 1)->get();
$breadcrumb = "<a href='/'>Home</a> / ".$product;
return view('pages.results')->with(compact('categories','productsAll','product','breadcrumb'));
}
My Category Model
class Category extends Model implements Searchable
{
protected $table = 'categories';
protected $fillable = [
'name'
];
public function categories(){
return $this->hasMany('App\Category','id');
}
public function products(){
return $this->hasMany('App\Product','id');
}
}
My Products Model
class Product extends Model implements Searchable
{
public function category() {
return $this->hasMany('App\Category', 'id') ;
}
public function attributes(){
return $this->hasMany('App\Product','id');
}
}
You have status column in more than one table.
Change this
->where('status', 1)->get();
to this
->where('products.status', 1)->get();
I was able to solve it by modifying my search function as follows.
public function searchProducts(Request $request) {
$product = $request->input('product');
$categories = Category::with('categories')->where(['parent_id' => 0])->get();
$productsAll = Category::query()->join('products', 'products.category_id', '=', 'categories.id')
->where('categories.name', 'LIKE', "%{$product}%")
->orWhere('products.product_name', 'LIKE', "%{$product}%")
->where('products.status', 1)->get();
$breadcrumb = "<a href='/'>Home</a> / ".$product;
return view('pages.results')->with(compact('categories','productsAll','product','breadcrumb'));
}

Get all data where pivot id (Laravel)

Is there the best way/the simplest way to get all data where pivot?
I tried this $article = Article::with('category')->wherePivot('category_id', $category)->get(); but i got error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'pivot' in 'where clause' (SQL: select * from `articles` where `pivot` = category_id)
The relation is many to many
Article
id
content
public function category(){
return $this->belongsToMany(Category::class, 'articles_has_categories', 'article_id', 'category_id');
}
Articles_Has_Categories
id
article_id
category_id
public function article ()
{
return $this->belongsTo(Article::class,'article_id');
}
public function category ()
{
return $this->belongsTo(Category::class,'category_id');
}
Category
id
name
public function article(){
return $this->belongsToMany(Article::class, 'articles_has_categories', 'category_id', 'article_id');
}
Please try this:
$article = Article::with(['category' => function($query) use ($category) {
$query->where('category_id', $category);
}
])->get();
$article = Article::with(['category' => function($query) use ($category) {
$query->whereHas('category_id', $category);
}
])->get();
If you use $category as array the use $query->whereHas('category_id', $category); or else if you use $category is a single id $query->where('category_id', $category);

Eager load relationships in laravel with conditions on the relation

I have categories related to each other in a tree. Each category hasMany children. Each end category hasMany products.
The products also belongsToMany different types.
I want to eager load the categories with their children and with the products but I also want to put a condition that the products are of a certain type.
This is how my categories Model looks like
public function children()
{
return $this->hasMany('Category', 'parent_id', 'id');
}
public function products()
{
return $this->hasMany('Product', 'category_id', 'id');
}
The Product Model
public function types()
{
return $this->belongsToMany(type::class, 'product_type');
}
In my database I have four tables:
category, product, type, and product_type
I've tried eager loading like so but it loads all the products and not just the ones that fulfil the condition:
$parentLineCategories = ProductCategory::with('children')->with(['products'=> function ($query) {
$query->join('product_type', 'product_type.product_id', '=', 'product.id')
->where('product_type.type_id', '=', $SpecificID);
}]])->get();
Instead of the current query, try if this fits your needs.
(I modified my answer as follows with your comment)
$parentLineCategories = ProductCategory::with([
'children' => function ($child) use ($SpecificID) {
return $child->with([
'products' => function ($product) use ($SpecificID) {
return $product->with([
'types' => function ($type) use ($SpecificID) {
return $type->where('id', $SpecificID);
}
]);
}
]);
}
])->get();
You can use whereHas to limit your results based on the existence of a relationship as:
ProductCategory::with('children')
->with(['products' => function ($q) use($SpecificID) {
$q->whereHas('types', function($q) use($SpecificID) {
$q->where('types.id', $SpecificID)
});
}])
->get();

Laravel Eloquent 5 Tables

I have 5 tables.
Users
Categories
Products
Product_categories
Order Details
A user purchases an an item and in my order details table I store the quantities etc.
I wanted to return all items that are of the main category = 'Testing' via the user.
$user = Auth::user();
return $user->items();
I have the following relationship on my user model.
public function items()
{
return $this->hasMany('App\OrderDetail','user_id')->selectRaw('item_description,count(quantity) as count')->where('item_description','<>','Carriage')->groupBy('item_id')->get();
}
I know I've not associated the the categories table here but I'm wondering how I would pull all the users order details where item category is "testing". The item can be related to many categories hence the product_categories table.
I'm not after someone writing the answer I'd like to know where I start to look at linking these via the model?
Would I be right in saying I have to do a function within my model relation?
According to your requirements & structure, your table should be structured like this:
users
id
name
...
categories
id
name
...
products
id
name
cost
...
category_product
id
category_id
product_id
order_details
id
user_id
cost
...
product_order_detail
id
product_id
order_detail_id
Your models should be structured like this:
class User extends Model
{
public function orderDetails()
{
return $this->hasMany(OrderDetail::class);
}
}
class Product extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class, 'category_product');
}
public function orderDetails()
{
return $this->belongsToMany(Order::class, 'product_order_detail');
}
}
class Category extends Model
{
public function product()
{
return $this->belongsToMany(Product::class, 'category_product');
}
}
class OrderDetail extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function products()
{
return $this->belongsToMany(Product::class, 'product_order_detail');
}
}
and to fetch all the items / products who belongs to the category named Testing and belongs to the user, who've ordered it:
$items = Product::whereHas('categories', function($q) {
$q->where('name', '=', 'Testing');
})->whereHas('orderDetails', function($q) use($user) {
$q->whereHas('user', function($q) use($user) {
$q->where('id', $user->id);
});
})->get();
Hope this helps!

Categories