In my Laravel 6.x project I have Product model, Warehouse and WarehouseProduct models.
In the Product I store the base information of my products. In the WarehouseProduct I store the stock amount informations about products in warehouse. Of course I have many warehouses with many products.
My Product looks like this:
class Product extends Model
{
protected $fillable = [
'name',
'item_number',
// ...
];
}
The Warehouse looks like this:
class Warehouse extends Model
{
protected $fillable = [
'name',
'address',
// ...
];
public function products() {
return $this->hasMany(WarehouseProduct::class);
}
public function missingProduct() {
// here I need to return a Product collection which are not in this Warehouse or the
// stored amount is 0
}
}
Finally the WarehouseProduct looks like this:
class WarehouseProduct extends Model
{
protected $fillable = [
'product_id',
'warehouse_id',
'amount',
// ...
];
public function product() {
return $this->belongsTo(Product::class, 'product_id');
}
public function warehouse() {
return $this->belongsTo(Warehouse::class, 'warehouse_id');
}
How can I get a Product collection which are not stored in a Warehouse or the amount is 0?
Something like this should work:
use App\Product;
public function missingProduct() {
$excludedProducts = $this->products()->where('amount', '>', 0)->pluck('id');
return Product::whereNotIn('id', $excludedProducts)->get();
}
Based on #KarolSobański's solution, when you add a warehouse_products relation to your product model:
use App\Product;
use Illuminate\Database\Eloquent\Builder;
public function missingProduct() {
return Product::whereDoesntHave('warehouse_products', function (Builder $query) {
$query->where('warehouse_id', $this->id);
})->orWhereHas('warehouse_products', function (Builder $query) {
$query->where('warehouse_id', $this->id);
$query->where('amount', 0);
})->get();
}
The shortest answer might be similar to this:
Product::doesntHave('warehouse_products')
->orWhereHas('warehouse_products', function (Builder $query) {
$query->where('amount', '=', 0)
})->get();
Although I am not sure if the above works.
But the following longer query certainly resolves the issue:
Product::where(function ($query) {
$query->doesntHave('warehouse_products');
})->orWhere(function ($query) {
$query->whereHas('warehouse_products', function (Builder $query) {
$query->where('amount', '=', 0);
});
})->get();
Related
In my Laravel 6.x project I have Product model, ProductCategory and WarehouseProduct models.
In the Product I store the base information of my products. In ProductCategory model I store the category informations of products. In the WarehouseProduct I store the stock amount informations about products in warehouse. Of course I have many warehouses with many products.
My Product looks like this:
class Product extends Model
{
protected $fillable = [
'name',
'item_number',
// ...
];
public function categories() {
return $this->belongsToMany(ProductCategory::class, 'product_category_products',
'product_id', 'product_category_id');
}
}
The ProductCategory looks like this:
class ProductCategory extends Model
{
protected $fillable = [
'name',
'description',
// ...
];
public function products() {
return $this->belongsToMany(Product::class, 'product_category_products',
'product_category_id', 'product_id');
}
}
The WarehouseProduct looks like this:
class WarehouseProduct extends Model
{
protected $fillable = [
'product_id',
'warehouse_id',
'amount',
// ...
];
public function product() {
return $this->belongsTo(Product::class, 'product_id');
}
}
I have this query now:
$query = WarehouseProduct::select([
'product_id',
'warehouse_id',
DB::raw('SUM(free_amount)'),
DB::raw('SUM(booked_amount)'),
// ...
]);
if (isset($request->warehouse_id)) {
$query->where([['warehouse_id', '=', $request->warehouse_id]]);
}
if (isset($request->product_category_id)) {
// ???
}
How can I add a where condition to the query what said: products from this category?
You can query the Relationship Existence. As it is a relationship through another model (Product) you could reduce the query if you defined that Has Many Through relationship, but I think that this will be enough for this particular query.
$warehouse_id = $request->warehouse_id;
$product_category_id = $request->product_category_id;
$query = WarehouseProduct::select([
'product_id',
'warehouse_id',
DB::raw('SUM(free_amount)'),
DB::raw('SUM(booked_amount)'),
// ...
])
->when($warehouse_id, function ($query) use ($warehouse_id) {
$query->where('warehouse_id', $warehouse_id);
})
->when($product_category_id, function ($query) use ($product_category_id) {
$query->whereHas('product', function ($que) use ($product_category_id) {
$que->whereHas('categories', function ($q) use ($product_category_id) {
$q->where('id', $product_category_id);
})
})
});
$results = $query->get();
Note that I am using the when method for the conditional clauses, but you can continue with the ifs as you was doing.
I'm developing a simple survey system, and I'm having problem with getting the right data.
I'm trying to retrieve all categories with questions and answers, that are assigned to a specific survey.
ERD:
The following code nearly works, however it does not filter the questions that are assigned to a specific survey.
$categories = Category::whereHas('questions.surveys', function ($query) use ($id) {
$query->where('surveys.id', $id);
})->with('questions', 'questions.answers', 'questions.surveys')
->get();
Question Model:
class Question extends Model
{
public function answers()
{
return $this->belongsToMany('App\Models\Surveys\Answer', 'question_answers');
}
public function category()
{
return $this->belongsTo('App\Models\Surveys\Category');
}
public function surveys()
{
return $this->belongsToMany('App\Models\Surveys\Survey', 'survey_questions');
}
}
Category Model:
class Category extends Model
{
public function questions()
{
return $this->hasMany('App\Models\Surveys\Question');
}
}
Survey Model
class Survey extends Model
{
public function questions()
{
return $this->belongsToMany('App\Models\Surveys\Question', 'survey_questions');
}
}
For this you need to constrain your eager loads as well:
$categories = Category::with([
'questions' => function ($query) use ($id) {
$query->with('answers', 'surveys')
->whereHas('surveys', function ($query) use ($id) {
$query->where('id', $id);
});
},
])->whereHas('questions.surveys', function ($query) use ($id) {
$query->where('id', $id);
})->get();
This way you're saying only get you the categories that are related to a specific survey and only get the question that relate to that category and the specific survey.
I'm working on a jobber search project online using Laravel 5.5.
In my project I want to make a search to find jobbers who live in a certain area and who perform a certain service, or where only one criteria matches.
I use three models: User, Area and Service.
Here is my search bar: I want to use this search bar to do it
This is the User model:
class User extends Authenticatable
{
use Notifiable, EntrustUserTrait;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name', 'surname', 'email', 'phone',
'password','type',];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [ 'password', 'remember_token',];
public function area(): BelongsTo
{
return $this->belongsTo(Area::class);
}
public function service(): BelongsTo
{
return $this->belongsTo(Service::class);
}
}
This is the Service model:
class Service extends Model
{
protected $fillable = ['category_id','name','description'];
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function users(): BelongsTo
{
return $this->belongsToMany(User::class, 'service_id');
}
public function jobs()
{
return $this->hasMany('App\Job');
}
}
And this is the Area model:
class Area extends Model
{
protected $fillable = ['town_id', 'name', 'description'];
public function town(): BelongsTo
{
return $this->belongsTo(Town::class);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'area_id');
}
}
Here is the controller code that did not work for me so far:
public function search(Request $request) {
$service = $request->get('service');
$area = Input::get('area');
if (!(empty($service)) && !(empty($area))) {
$results = User::with(['area', 'service'])
->where('area_id', 'like', "%$area%")
->whereHas('service', function ($query) use ($service) {
$query->where('category_id', $service);
})
->paginate(10);
return view('Search.search', compact('results'));
} elseif (!(empty($service)) && empty($area)) {
$results = User::with(['area', 'service'])
->whereHas('service', function ($query) use ($service) {
$query->where('category_id', $service);
})
->paginate(10);
return view('Search.search', compact('results'));
} elseif (empty($service) && !empty($area)) {
$results = User::with(['area', 'service'])
->where('area_id', 'like', "%$area%")
->paginate(10);
return view('Search.search', compact('results'));
}
}
I would advice you to build your query dynamically depending on the available input. This reduces your code and makes sure you will not have to add new code in multiple places should you extend your search in future.
public function search(Request $request)
{
$query = User::with(['area', 'service']);
if ($request->filled('service')) {
$query = $query->whereHas('service', function ($q) use ($request) {
$q->where('category_id', $request->get('service'));
});
}
if ($request->filled('area')) {
$query = $query->where('area_id', $request->get('area'));
}
$results = $query->paginate(10);
return view('Search.search', compact('results'));
}
As long as you don't call get(), paginate() or find() on the $query, it will be a Illuminate\Database\Eloquent\Builder object. This means you can add additional conditions on the query which will all be included in the actual SQL query and are not performed in-memory (which you clearly don't want).
The method $request->filled('service') will check both of the following two conditions:
$request->has('service')
!empty($request->get('service'))
If you want to be able to search Areas by name, you might need to change the if($request->filled('area')) { ... } part to the following:
if ($request->filled('area')) {
$query = $query->whereHas('area', function ($q) use ($request) {
$q->where('name', 'like', '%'.$request->get('area').'%');
});
}
I'm trying to retrieve some results from a model using a relation and I'm trying to apply some filters on that relationship.
Here is the model:
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserProduct extends Model
{
protected $primaryKey = null;
public $incrementing = false;
protected $table = "user_product";
public $fillable = [
...
"product_id",
"user_id",
"is_featured",
"is_hidden_from_latest"
...
];
public function product()
{
return $this->belongsTo("\\App\\Models\\Product", "product_id", "id");
}
...
}
and here is the related model:
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $table = "products";
public $timestamps = false;
public $fillable = [
...
];
public function userProduct()
{
return $this->hasOne("\\App\\Models\\UserProduct", "product_id", "id");
}
...
}
Here is the query on UserProduct model and product relationship:
$user_products = UserProduct::with("product")
->whereHas("product", function($q) {
$q->where("product_status", "live")
->where("parent_child", "Child");
})->where("is_featured", 1)
->orWhere("is_hidden_from_latest", 0)
->orderBy("is_featured", "desc")
->orderBy("updated_at")
->get();
The problem is that whereHas subquery doesn't seem to filter anything no matter what value to compare to I use for each product_status and parent_child.
Is there something that I don't do correctly?
Update: Seems that the game breakers are these two where() statements at the end:
....
->where("is_featured", 1)
->orWhere("is_hidden_from_latest", 0)
....
and more specifically the orWhere() statement.
Try this way
$user_products = UserProduct::with("product")
->whereHas("product", function($q) {
$q->where("product_status", "live")
->where("parent_child", "Child");
})
->where(function ($query) {
$query->where("is_featured", 1)
->orWhere("is_hidden_from_latest", 0);
})
->orderBy("is_featured")
->orderBy("updated_at")
->get();
I just removed where("is_featured", 1) and replaced it with just where("is_hidden_from_latest", 0) as I order the results ascendantly by is_featured anyway.
The whereHas() subquery works properly. :)
I am using Laravel 5.2 ,how to write this query?
There are two tables,user and articles,they have a one-to-many relationship.
I want to query users according to these conditions:
1、Show users who have articles,not show users who have not articles.
2、Articles contain two types,published and not published, "1" indicates published,show published articles ,not show articles which not published.
3、30 users are shown per page.
Like this, it's not right ,how to modify it?
HomeController:
public function index()
{
$users = User::with('articles')->where('is_published','=',1)->paginate(30);
return view('index', compact('users'));
}
User:
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, HasRoleAndPermissionContract
{
use Authenticatable, CanResetPassword, HasRoleAndPermission;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
public function articles()
{
return $this->hasMany(Article::class);
}
}
Article:
class Article extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
//edit-1:
//Scope a query to only include status=1.
public function scopeStatus($query)
{
return $query->where('status',1);
}
}
edit:
#SSuhat #RamilAmr Thanks! If there is a Local Scope in Model "Article",how to modify the answer:
$query = User::with(['articles' => function ($q) {
$q->where('is_published', 1);
}])
->has('articles') //<<<<<<<<<
->paginate(30);
return $query;
Try this:
$query = User::with(['articles' => function ($q) {
$q->where('is_published', 1);
}])->paginate(30);
return $query;
if the user has not any articles,the user will not be shown,how to filter?
Try this code:
$query = User::with(['articles' => function ($q) {
$q->where('is_published', 1);
}])
->has('articles') //<<<<<<<<<
->paginate(30);
return $query;
$query = User::with('articles')->wherehas('articles', function ($q) {
$q->where('is_published', 1);
})->paginate(30);
return $query;
I hope this helps you.