Passing array in many to many relationship to the controller- laravel - php

I have a many to many relationship between Category and posts tables like this:
Category class:
class Category extends Model {
public function posts()
{
//return $this->hasMany('App\Posts','category_id');
return $this->belongsToMany('App\Posts', 'categories_post', 'category_id', 'post_id')
->withTimestamps();
}
}
Posts class:
class Posts extends Model {
public function category()
{
//return $this->belongsTo('App\Category','category_id');
return $this->belongsToMany('App\Category', 'categories_post', 'post_id', 'category_id')
->withTimestamps();
}
}
When I need to access only one category's posts I do this in my controller:
public function cat($category_id)
{
$posts= $category_id->posts()->paginate(7);
return view('cat')->with('posts',$posts);
}
Edit
to make this work, I added this in "RouteServiceProvider.php" file:
public function boot(Router $router)
{
parent::boot($router);
$router->model('categories','App\Category');
}
and this works perfectly. The problem is, I have another controller which should get the posts for multiple categories :
public function index()
{
$title = 'news';
$categories= [1, 2, 10, 11];
// Here is the problem:
$posts= $categories->posts()->paginate(7);
return view('news')->with('posts',$posts)->with('title',$title);
}
This gives me this error: Call to a member function posts() on a non-object
I know there is something wrong with calling the array, but I don't know how to solve it. Can any one help me please :)
The solution
after what Thomas Van der Veen said in his answer , I came up with this controller which is working perfectly:
public function index()
{
$title = 'news';
$category_ids = [1, 2, 10, 11];
$posts = Posts::whereHas('category', function($query) use ($category_ids) {
$query->whereIn('id', $category_ids);
})->get();
return view('news')->with('posts',$posts)->with('title',$title);
}

You could do something like:
$category_ids = [1, 2, 10, 11];
$posts = Post::whereHas('categories', function($query) use ($category_ids) {
$query->whereIn('id', $category_ids);
});
See this.

Related

How to get posts by current tag and category simultaneously in Laravel?

I have Post, Tag and Category models. I know how to get posts by tag or posts by category. But I need to get posts by chosen tag and category simultaneously (at one time). Can somebody help? I use Laravel 8.
Post model
class Post extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function tags()
{
return $this->belongsToMany(
Tag::class,
'post_tag',
'post_id',
'tag_id'
);
}
}
Tag model
class Tag extends Model
{
public function posts()
{
return $this->belongsToMany(
Post::class,
'post_tag',
'tag_id',
'post_id'
)->withTimestamps();
}
}
Category model
class Category extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
Post controller's methods
class PostController extends Controller
{
public function show($slug)
{
$post = Post::where('slug', $slug)->orderBy('date', 'desc')->firstOrFail();
return view('pages.post', compact('post'));
}
public function tag($slug)
{
$tag = Tag::where('slug', $slug)->firstOrFail();
$posts = $tag->posts()->orderBy('date', 'desc')->paginate(7);
return view('pages.postlist', ['posts' => $posts]);
}
public function category($slug)
{
$category = Category::where('slug', $slug)->firstOrFail();
$posts = $category->posts()->orderBy('date', 'desc')->paginate(7);
return view('pages.postlist', ['posts' => $posts]);
}
}
Routes
Route::get('/posts/{slug}', 'App\Http\Controllers\PostController#show')->name('post.show');
Route::get('/posts/tag/{slug}', 'App\Http\Controllers\PostController#tag')->name('ptag.show');
Route::get('/posts/category/{slug}', 'App\Http\Controllers\PostController#category')->name('pcategory.show');
You might want to use whereHas:
$categoryId = 1;
$tagId = 2;
Post::whereHas('category', function ($query) use ($categoryId) {
$query->whereKey($categoryId);
})->whereHas('tags', function ($query) use ($tagId) {
$query->whereKey($tagId);
})->get();

Call to undefined method App\Product::whereHas() in laravel

Hi try to get my products list in subcategory page but i'm getting this error
Call to undefined method App\Product::whereHas()
here is my function
// Subategory page included posts
public function productsubcategory($slug)
{
$products = Product::whereHas('subcategory.category', function($q) use($slug){
$q->where('slug',$slug);
})->paginate(6);
return view('frontend.subcategories', compact('products'));
}
this is my route:
Route::get('/category/{slug}/subcategory/{subslug}', ['as' => 'showbysub', 'uses' => 'IndexController#productsubcategory']);
my product model:
public function categories(){
return $this->belongsTo(Category::class);
}
public function subcategories(){
return $this->belongsTo(Subcategory::class);
}
category model:
public function products(){
return $this->hasMany(Product::class);
}
public function subcategories(){
return $this->hasMany(Subcategory::class);
}
subcategory model:
public function category(){
return $this->belongsTo(Category::class, 'category_id');
}
public function products(){
return $this->hasMany(Product::class);
}
any idea?
UPDATE:
I changed my function to:
public function productsubcategory($slug)
{
$subcategories = Subcategory::where('slug','=',$slug)->with('products')->paginate(6);
return view('frontend.subcategories', compact('subcategories'));
}
and now my page is loading but will not show any item (product).
UPDATE 2
I changed my function to this:
public function productsubcategory($slug)
{
$products = Product::with('subcategories')
->orderBy('id', 'desc')
->get();
return view('frontend.subcategories', compact('products'));
}
Now I can get products but the problem is all products will show even
if they're included other categories.
How to solve that?
I don't understand what do you mean in this lines
$products = Product::whereHas('subcategory.category', function($q) use($slug){
$q->where('slug',$slug);
})->paginate(6);
Refer to https://laravel.com/docs/5.4/eloquent-relationships. If you are trying to querying relationship, you can do like this:
$products = Product::whereHas('subcategories', function($q) use($slug){
$q->where('slug',$slug);
})->paginate(6);
or
$products = Product::whereHas('categories', function($q) use($slug){
$q->where('slug',$slug);
})->paginate(6);
The relationship name must the same with relationship method in your Product model
SOLVED
public function productsubcategory($slug, $subslug)
{
$products = Product::whereHas('subcategory', function($q) use($subslug){
$q->where('slug',$subslug);
})->get();
return view('frontend.subcategories', compact('products'));
}
and product model from subcategories change to subcategory

Eloquent WHERE NOT with relationships

I am using Lumen and it's Models with model's relationships. What I am trying to do is to get all Categories and count how many posts every category has, which is working fine. However, I do not want it to count those which creator_id == $current_user_id, how should I do it?
This is in my Post class:
public function user(){
return $this->belongsTo(User::class);
}
public function category(){
return $this->belongsTo(Category::class);
}
And this is in my Category class:
public function posts(){
return $this->hasMany(Post::class);
}
And this is my query:
public function getCategories($user_id){
$categories = Category::withCount('posts')->get();
return new JsonResponse(['categories' => $categories]);
}
So I do not want to count posts that the current user has created.
So the output should be something like this:
{
"categories": [
{
"name": "All",
"posts_count": 0
}]
}
This what I also tried without success:
$categories = Category::where('creator_id', '!=', $user_id)->withCount('posts')->get();
Try using the following line:
$categories = Category::withCount(['posts' => function($query) use($user_id){
$query->where('creator_id', '!=', $user_id);
}])->get();

Global Query Scope and Relationship with Laravel 5

I'm trying to get familiar with eloquent and I've been playing around with some global query scopes, but i'm not having much success when it comes to the effect that it has on relationships.
I have two models; product and category, each with some global query scopes added.
Products:
use productConditions;
protected $table = 'product';
public $timestamps = false;
private $websiteDetails;
public function __construct(){
parent::__construct();
$this->websiteDetails = session('website');
}
public function scopeByCategory($query, $categoryId){
return $query->whereHas('categories', function($q) use ($categoryId){
$q->where('id', $categoryId);
});
}
public function categories(){
return $this->belongsToMany('App\Category', 'product_category', 'product_id', 'category_id');
}
category:
use categoryConditions;
protected $table = 'category';
public $timestamps = false;
public function products() {
return $this->belongsToMany('App\Product', 'product_category', 'category_id', 'product_id');
}
I'm using traits to boot the global scopes and the files are as follows:
So for products:
public function apply(Builder $builder, Model $model)
{
$builder->where('state', 'a');
$builder->where('stock_level', '>', 0);
}
public function remove(Builder $builder, Model $model){
$query = $builder->getQuery();
foreach((array) $query->wheres as $key => $where){
if($where['column'] == 'state'){
unset($query->wheres[$key]);
}
if($where['column'] == 'stock_level'){
unset($query->wheres[$key]);
}
}
$query->wheres = array_values($query->wheres);
}
and for categories
public function apply(Builder $builder, Model $model)
{
$websiteDetails = session('website');
$builder->where('website_id', $websiteDetails['id']);
}
public function remove(Builder $builder, Model $model){
$query = $builder->getQuery();
foreach((array) $query->wheres as $key => $where){
if($where['column'] == 'website_id'){
unset($query->wheres[$key]);
}
}
$query->wheres = array_values($query->wheres);
}
Because there are multiple records for the category field with a specific id, due to the face that there are multiple website profiles. I wanted to set a global query scope for categories -> website_id.
So this works beautifully when doing some like this:
$category = Category::with('products')->first();
$category->products;
It gets all the categories with the specified website_id and then pulls in the products.
However, it doesn't work, when I set up a query scope in the model, to do essentially the same thing, but the other way round. So, this doesn't work:
$category = Product::byCategory(2)->get();
Unless, I delete the global query scope in the category model and modify the whereHas closure to:
public function scopeByCategory($query, $categoryId){
return $query->whereHas('categories', function($q) use ($categoryId){
$q->where('id', $categoryId)->where('website_id', $this->websiteDetails['id']);
});
}
but doing it this way, means I can no longer query the Category model, without setting up some sort of byWebsite query scope method.
Could somebody tell me if I'm somehow doing it wrong, or suggest another solution to my problem.
Many Thanks
Try overwriting the query method in the category model, it should work for the categories.
public function newQuery($excludeDeleted = true)
{
$websiteDetails = session('website');
return parent::newQuery()->where('website_id', $websiteDetails['id']);
}
If it doesnt work as product relation try this:
public function categories(){
return $this->belongsToMany('App\Category', 'product_category', 'product_id', 'category_id')
->where('website_id', $this->websiteDetails['id']);
});
}

Laravel 4.1 eager loading

I'm having trouble on the eager loading.
Let's say I have models of Members, TrainingCategory, TrainingCategoryResult and Registration
Member Model:
public function registration() {
return $this->hasMany('Registration', 'member_id');
}
public function trainingResults(){
return $this->hasMany('trainingResult', 'member_id');
}
public function trainingCategoryResults() {
return $this->hasMany('TrainingCategoryResult', 'member_id');
}
TrainingCategory Model:
public function trainings() {
return $this->hasMany('Training', 'id');
}
public function trainingCategoryResults() {
return $this->hasMany('trainingCategoryResult', 'category_id');
}
TraningCategoryResult Model:
public function category() {
return $this->belongsTo('TrainingCategory', 'id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
Registration Model:
public function course() {
return $this->belongsTo('Course', 'course_id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
I am trying to eager load all the registration info and its related info including the TraningCategoryResult info but I not sure how to get that TraningCategoryResult which required two foreign keys (category_id and member_id), is there any way to do that?
Here is my code atm:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
});
$members= $members
->with('registration.course',
'registration.course.traningCategories',
->get(['member.id']);
Thank you.
This will not work Member::with('categoryResult')->with('registration')->get()
You can make a new relation in Member Model
public function categoryResult()
{
return $this->belongsTo('Category')->with('Registration');
}
//and then call
Member::with('categoryResult')->get();
You could use a few options to achieve that:
OPTION 1: create a variable relationship
Change your relation in the Member model
public function trainingCategoryResults($category_id = null) {
if(empty($category_id))
return $this->hasMany('TrainingCategoryResult', 'member_id');
else
return $this->hasMany('TrainingCategoryResult', 'member_id')
->where('category_id', $category_id);
}
The code above might have limitations and it doesn't take advantage of many laravel features, but it will work.
OPTION 2: Access from relationship
You can keep everything as is, and load as follow:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
})
->where('id', $member_id) // set the id of the memeber
->with(array(
'traningCategoryResults' => function($q)use($category_id){
$q->where('category_id', $category_id); // This makes sure you get only desired results
}
))
In this way you will have only what you need, assuming you know the $category_id

Categories