Laravel HasMany relationship not giving expected result - php

I have three table with following columns:
posts table
id
author
title
content
seen
post_img
post_category table
id
category_id
post_id
categories table
id
category
I created post_category table to store post catergories. I wanted to get posts that have specific category. And i created my PostModel associated with posts table like this:
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\AuthorModel;
use App\Models\PostCategoryModel;
use App\Models\CategoryModel;
class PostModel extends Model
{
use HasFactory;
protected $table = 'posts';
public function getAuthor(){
return $this->hasOne(AuthorModel::class, 'id', 'author');
}
public function getCategories(){
return $this->hasMany(PostCategoryModel::class, 'post_id', 'id');
}
}
After i got the PostCategoryModel from calling getCategories method, i created PostCategoryModel associated with post_category table like this:
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\CategoryModel;
use App\Models\PostModel;
class PostCategoryModel extends Model
{
use HasFactory;
protected $table = 'post_category';
public function getCategory(){
return $this->hasMany(CategoryModel::class, 'id', 'category_id');
}
public function getPosts(){
return $this->belongsToMany(PostModel::class, 'id', 'post_id');
}
}
But here is the problem, when i call PostModel::getCategories in my Controller method it gives me this error: Undefined constant App\Models\PostModel::getCategories. How can i get categories of a post in my case?
Laravel version: Laravel Framework 8.22.1
PHP version: PHP 8.0.1 (cli) (built: Jan 5 2021 23:43:33) ( NTS Visual C++ 2019 x64 )

As I know you can't call relation methods like static method cause it needs an instance to check its id and match with other table. You can use something like this:
$cats = PostModel::find(1)->getCategories();
But it's just an example and in real application you probably use route model binding or anything else.

I believe you have a many to many relation between post and category, If so then you don't need intermediate model for junction table post_category unless you have another specific reason to introduce model for this table.
For many to many relationship you need to adjust your post and category models as
class Post extends Model
{
//.. Other methods
public function categories(){
return $this->belongsToMany(Category::class, 'post_category', 'post_id', 'category_id');
}
}
class Category extends Model
{
//.. Other methods
public function posts(){
return $this->belongsToMany(Post::class, 'post_category', 'category_id','post_id');
}
}
I wanted to get posts that have specific category
Once you have proper relations in place you can get posts for specific category using whereHas()
$posts = Post::whereHas('categories', function (Builder $query) use($category_id) {
$query->where('id', $category_id);
})->get();
How can i get categories of a post in my case?
To get categories associated with posts you can eager load them as
$posts = Post::with('categories')
->whereHas('categories', function (Builder $query) use($category_id) {
$query->where('id', $category_id);
})->get();

Related

Retrieve data from database filtered by pivot table Laravel

I've got table structure like this:
users:
id
name
managers:
id
user_id
user_manager:
manager_id
user_id
vacations:
user_id
date_from...
My task is to fetch all data from vacations table where user (vacations.user_id) belongs to given manager id (managers.user_id) and that assigment is in pivot table user_manager. User can have many managers, manager can have many users. I've been trying to do this, but I've got stuck. My workaround was this:
DB::table("vacations")
->leftJoin("users", function ($join) {
$join->on("vacations.user_id", "=", "users.id");
})
->leftJoin("user_manager", function ($join) {
$join->on("user_manager.user_id", "=", "users.id");
})
->where("user_manager.manager_id", "=", $id)
->get();
But I want to know a way to make this with eloquent model and all of it's fancy stuff.
Manager model:
class Manager extends Model
{
use HasFactory;
protected $table = 'managers';
protected $fillable = ['user_id'];
public function users()
{
return $this->belongsToMany(User::class, 'employee_manager')->withTimestamps();
}
}
User model:
class User extends Model
{
use HasFactory, Notifiable;
public function managers()
{
return $this->belongsToMany(Manager::class, 'employee_manager')->withTimestamps();
}
}
You are trying to get all Vacation records which created by a User which belongs to a specific Manager
It means you need to get all Vacations where user_id in that Manager
's users()
You can achieve this with a basic way like this;
$vacationsThatConcernTheManager = Vacation::whereIn('user_id', $manager->users()->pluck('id'))->get();
And if you want use directly something like $manager->vacations() you can use hasManyThrough as described in documentation.
Add this relation to your Manager model;
public function vacations()
{
return $this->hasManyThrough(Vacation::class, User::class);
}

Laravel queries (function find() ) in Laravel throught using polymorphic ralationship

I'm quite new in Laravel and have some question about Laravel SQL query through using polymorphic relationship
The situation is:
I have two different tables in my SQL.
One table 'estates' contains a list of estates and their parameters
Another table is 'type_categories' which contains a list of type categories
Every estate can be related to many type categories and every type category can have many estates (so I use polymorphic relationships many to many).
They are related through the third table ''
So, as a result, I need to do a query, which will find all estates which is related to category, but my user can choose a numerous of category, not just one.
I tried to use function find(1)->estates; which as a parameter have an id of type category, which related estates I tried to find. It works fine, but the problem is, that my user needs to find an estate to several categories and function, but function finds works with just one parameter in the situation we nave polymorphic relation, not with the several parameters as I need.
Is there some way to use several parameters in function find() and also use polymorphism?
Sure, that there exist some other functions for it...
Then I show you my code.
Estates model looks:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Estate extends Model
{
protected $fillable = ['title', 'slug', 'estate_text', 'created_by', 'modified_by'];
public function categories() {
return $this->morphToMany('App\TypeCategory', 'categoryable');
}
}
Type categories model looks:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class TypeCategory extends Model
{
protected $fillable = ['title', 'slug', 'parent_id', 'created_by', 'modified_by'];
public function estates() {
return $this->morphedByMany('App\Estate', 'categoryable');
}
}
And finally, estates controller where I tried to select estates model looks:
use Illuminate\Http\Request;
use App\Estate;
use App\TypeCategory;
class EstateController extends Controller {
public function searchEstate() {
$estates = TypeCategory::find(1)->estates;
return view('main', [
'estates' => $estates
] );
}
}
I don't sure it is your problem or not.
try this:
$estates = Estate::whereHas('categories', function ($query) {
$query->whereIn('slug', ['xxx','yyy','zzz']);
})->get();

Retrieve post by category id laravel

I want to retrieve all post with given category,
i am developing api, at input i will receive category name, and then i want to retrieve that category by its id in posts table,i want to do this because if some changed category name in the future i dont have to change that in my code
i have create like this
PostController.php
public function getPostsByCategory(Request $request)
{
$posts=Posts::where('category',$request->category)->get();
return response()->json(['posts'=>$posts]);
}
Post model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Posts extends Model
{
protected $fillable = [
'post_title','post_description', 'category', 'user_id',
];
Category Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Posts_Category extends Model
{
}
can you please help me with this, please suggest me any idea
i think you are looking for with eager load function
You must store category id not the name of the category..
class Posts extends Model
{
protected $fillable = [
'post_title','post_description', 'category_id', 'user_id',
];
public function category()
{
return $this->belongsTo(Posts_Category::class, 'category_id', 'id');
}
}
use as
$posts = Posts::where('category_id',$request->category)->with('category')->get();
If you really want to search by name
$posts = Posts::whereHas('category', function ($q) {
$q->where('category_name', 'like', "%{$request->category}%");
})->get();
You can fetch the category by its name first, and then filter posts by category id
public function getPostsByCategory(Request $request)
{
$category = Posts_Category::whereName($request->category)->firstOrFail();
$posts=Posts::where('category',$category->id)->get();
return response()->json(['posts'=>$posts]);
}
you're doing this the wrong way from the get go. best practice is to store category_id in posts table, of course this is only if you need a one-to-many relation between posts and categories table. plus I wouldn't recommend to break the convention of naming models unless you have no other way. I suggest you to read this section more thorough
https://laravel.com/docs/5.6/eloquent-relationships
Please i need a fix solution to this, i want to fetch all the latest post from a specific category in my laravel blog project
These are my models
Category model
public function posts()
{
return $this->belongsToMany('App\Post')->withTimestamps();
}
Post model
public function categories()
{
return $this->belongsToMany('App\Category')->withTimestamps();
}
What's they next thing to do?
please help
$posts = Posts::where('category_id', $category->id)->get();

Calling a list of posts based on category

Im trying to call a list os posts based on a category, but it isnt working, is giving me the error:Non-static method App\Category::posts() should not be called statically, assuming $this from incompatible context.
I already tried calling from both sides, going threw the post model methdos or categories, but same error message. Can figure out what im doing wrong.
Database:
Posts:
-id;
-title;
-text;
Categories:
-id;
-name;
-internal_name;
category_post:
-id;
-post_id;
-category_id
Controller:
public function categoryPostList($category)
{
$category = Category::posts()->where('internal_name',$category)->first();
$posts = Post::categories()->where('category_id',$category->id)->get();
dd($posts)
}
Model Post:
class Post extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Model Category:
class Category extends Model
{
public function posts()
{
return $this->belongsTo('App\Post');
}
}
First of all if relationship is many to many (you're using pivot table) your relation should be belongsToMany(), but not belongsTo().
To load relation You should use with() method. For example, if you want to load one category with all it's posts:
Post::where('category_id', $category->id)->with('categories')->first();
You can use whereHas as:
$category = Category::where('internal_name',$category)->first();
$posts = Post::whereHas('categories', function($q) use($category) {
$q->where('category_id',$category->id);
})->get();
Or
$category = Category::where('internal_name',$category)->first();
$posts = $category->posts;
The whereHas method is used to put "where" conditions on your has
queries.

Getting all objects from hasMany relation and paginate

In my Laravel application, I have a category listing page. When the user clicks on a certain subcategory, I'd like to list all the products and use pagination on that result. I'm already listing all the products related to that subcategory, for now, with the help of a subcategory ID:
public function subcategoryListing($slug){
$products = Subcategory::find($idofSubcat)->products;
return view('pages.subcategorylisting')
->with(array(
'products' => $products,
));
}
There are three classes involved in this structure: Category, Subcategory and Products. They were declared as follows:
Category
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use App\Subcategory;
class Category extends Model
{
protected $table = 'category';
public $timestamps = false;
public function subCategory(){
return $this->hasMany('App\Subcategory', 'category_id');
}
}
Subcategory
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Subcategory extends Model
{
protected $table = 'subcategory';
public $timestamps = false;
public function products(){
return $this->hasMany('App\Products');
}
}
Products
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Products extends Model
{
protected $table = 'products';
}
For each model class I have one table, with this structure:
Category
- id
- category_name
SubCategory
- id
- category_id
- subcategory_name
Products
- id
- subcategory_id
- product_title
- description
- price
What I want is to paginate the results retrieved from the query in my page. Is there any better way to fetch the products associated to the subcategory and paginate them?
In Eloquent (Laravel's ORM) when you call a relation as a property ($subCategory->products), it returns the related object or a collection of objects depending on the relation type (belongs to, has many, ...). Instead, if you call it as a function ($subCategory->products()), you get a QueryBuilder instance.
Refer to http://laravel.com/docs/5.1/eloquent-relationships#querying-relations, under section Relationship Methods Vs. Dynamic Properties for more details on that.
Anyway, using the relationship method, you can call paginate() for your collection. Then, with that in mind, you can change your code slightly to get what you want:
public function subcategoryListing($slug) {
// I'm supposing here that in somewhere before
// run the query, you set the value to $idofSubcat
// variable
$products = Subcategory::find($idofSubcat)->products()->paginate();
return view('pages.subcategorylisting')
->with(array(
'products' => $products,
));
}
I think, that create two categories table not so correctly, it will be better to use next:
table categories
id category_name parent_id(nullable)
and Products
id category_id product_title description price
It's more usefull, you can remove one Subcategory model and do all in Category.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $table = 'category';
public $timestamps = false;
public function subCategory(){
return $this->belongsToMany('App\Category', 'categories', 'id', 'parent_id');
}
public function products(){
return $this->hasMany('App\Products');
}
}
and products model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Products extends Model
{
protected $table = 'products';
public function category() {
return $this->belongsTo('App\Category')
}
}
then, you can get the query result
public function subcategoryListing($slug){
$products = Category::find($idofSubcat)->products;
return view('pages.subcategorylisting')
->withProducts($products); // it's a magic))
}
But, existing one not pretty thing. Do you really sure, that products will be only in one category?)
I couldn't really understand some of your initial function:
// $slug is not being used anywhere within the function
public function subcategoryListing($slug){
// $idOfSubcat isn't passed to this function so will throw an error
$products = Subcategory::find($idofSubcat)->products;
return view('pages.subcategorylisting')
->with(array(
'products' => $products,
));
}
If all you're trying to do is paginate the Products that belong to a specific subcategory_id, given you have the subcategory_id then the following code will work:
$products = Product::where('subcategory_id', $idofSubcat)->paginate();
Then you can return this paginated collection to your view as you're already doing:
return view('pages.subcategorylisting')
->with(compact('products'));

Categories