I have 3 tables:
products
|id|name|about|
=categories=
|id|name|parent|
=products-categories=
|id|product_id|cat_id|
I need to take a product categories names. I have a sql query:
SELECT s.name FROM products AS p
LEFT JOIN `products-categories` AS cats ON p.id = cats.product_id
LEFT JOIN `categories` AS s ON cats.cat_id = s.id
WHERE product_id = 1;
And It works! But how I can do this with the help of Laravel Eloquent (Not Fluent!)
You can use Eloquent relationship and in this case, create two models, for both tables, i.e. product and Category:
class Category extends Eloquent {
protected $table = 'categories'; // optional
public function products()
{
return $this->belongsToMany('Product', 'products_categories', 'category_id', 'product_id');
}
}
class Product extends Eloquent {
protected $table = 'products'; // optional
public function categories()
{
return $this->belongsToMany('Category', 'products_categories', 'product_id', 'category_id');
}
}
Now you may use these relationship methods to get related data, for example:
$product = Product::with('categories')->find(1);
This will return the product with id 1 and all the related categories in a collection so you may use $product->categories->first()->name or you may do a loop on the categories like:
foreach($product->categories as $cat) {
echo $cat->name;
}
Also you may use join which doesn't require the relationship methods and you may use same approach to join the models that is used in Fluent (Check other answer). But either way, you need to store the category and product mappings in the products_categories table. Read more about many-to-many relationship on Laravel website.
Just use the leftJoin method. It works the same in Eloquent as in Query Builder.
$product = Product::leftJoin('product-categories', 'product-categories.product_id', '=', 'products.id')
->leftJoin('categories', 'categories.id', '=', 'product-categories.cat_id')
->where('product_id', 1)
->first(['categories.name']);
Related
I'm making an attempt to write Eloquent join query but i didn't get the result i wanted.
i have 2 table
products which contains all information about products
category_product contains product_id and category_id
i just want to select all information about products which their category_id is equal to 2
After you have belongsToMany relation between categories and products.
Product::whereHas('categories', function($query){
$query->where('id',2);
})->with('categories')->get();
I can offer you a solution like this
class Product extends Model
{
public function category()
{
return $this->morphToMany(Category::class, 'category_product table');
}
}
class Category extends Model
{
public function product()
{
return $this->morphedByMany(Product::class, 'category_product table');
}
}
in Controller
$products = DB::table('products')
->join('category_product', 'products.id', '=', 'category_product.products_id')
->select('products.*')
->where('category_product.category_id','=',2)
->get();
I'm still a beginner in Laravel. I'm trying to write a query to get categories which are associated with specific place. I have the following three tables
place place_categorye category
------ ---------------- -------------
id place_id id
name category_id name
each place has number of categories
what I want to do is when I choose place_id I get the categories associated with it in the pivot table.
I supposed that you have a Many To Many relation between places and categories then in your Place Model
public function categories(){
return $this->belongsToMany(Category::class, 'place_categorye', 'place_id', 'category_id');
}
And now you can access the categories of a certain Place like below:
$place->categories;
Without the relationship definitions, it is hard to give an answer on the ORM queries, but here is a raw query which will give you the expected result,
DB::select(DB::raw("
select category.id, category.name
from place
join place_categorye on place_categorye.place_id = place.id
join category on category.id = place_categorye.category_id
where place.id = 1");
if you want the results inclusive of all the null values, depending on the use case you can use a left join instead of join (join means innter join by default)
** to many relationship** for this
<?php
namespace App\PlaceCategory;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PlaceCategory extends Pivot {
public function place()
{
return $this->belongsTo('App\Place');
}
public function category()
{
return $this->belongsTo('App\Category');
}
}
category Model
public function places()
{
return $this->hasMany('App\Place')
->using('App\PlaceCategory');
}
Place Model
public function categpries()
{
return $this->hasMany('App\Category')
->using('App\PlaceCategory');
}
Now you can access this easily & can query easily .
Like
$place=Place::with('categories')->first();
by using
$place->categories we get all the categories for this place
I have two models, Book and Author. Book belongsTo Author (I've trimmed some of the crud from other parts):
class Book extends Model {
protected $table = 'books';
protected $fillable = ['title'];
public function author(){
return $this->belongsTo('Author', 'author');
}
}
class Author extends Model {
protected $table = 'authors';
protected $fillable = ['name'];
}
If I want to get all books ordered by their title, I would use this:
Books::with(['author'])->orderBy('title', 'asc')->get();
Is it possible to order those books by the author's name? I've tried many combinations:
Books::with(['author' => function($query){
$query->orderBy('name', 'asc');
}])->get();
Books::with(['author'])->orderBy('name')->get();
Books::with(['author'])->orderBy('author.name')->get();
Books::with(['author'])->orderBy('authors.name')->get();
But none worked. The first, using the with() query, orders all authors before joining them into the books collection, I think. The others threw 500 errors.
If this were plain MySQL, I'd write something like this:
select * from books join authors on books.author_id = authors.id order by authors.name asc;
Is this possible using Builder/Eloquent in laravel 5.1? Do I need to use a DB query?
Enabling eager load by calling with('author') will load authors in a separate query, that's why author's columns are not available and sorting by them does not work. Explicit join is needed.
In order to sort by a relation you need to join your books with their authors:
Book::select('books.*')
->join('authors', 'authors.id', '=', 'books.author_id')
->orderBy('authors.name')
->get();
eloquent return a collection its very powerful see the docs for things you can do with that
so to order the books by author name you can do:
function orderBooksByAutherName($books){
return $books->sortBy(function ($book, $key) {
return $book->author->name;
});
}
if you really want to use join the only option is to use the query builder
I will describe this situation with more details:
I have a list of products where every product belongs to a certain category and to a certain brand. Some of the products can get reviewed by users.
On /reviews/ page in my Laravel application, I have a list of reviews and select boxes for category and brand along with search button of course.
If user doesn't choose category or brand, all reviews get displayed, paginated, and that's good.
The problem arises when user chooses either category or brand or both and tries to get all the reviews filtered that way.
Reviews table fields: ID, user_id(foreign key users.ID), product_id(foreign key products.ID), text
Products table fields: ID, category_id(foreign key categories.ID), brand_id(foreign key brands.ID), name
Categories table fields: ID, name
Brands table fields: ID, name
Users table fields: ID, username
When I'm listing reviews, I'm simply using:
$reviews = Review::orderBy('id', 'DESC')->paginate(5);
If I would like to filter reviews by user_id, that would be easy as the reviews table contains user_id column,
but, how to filter them by product category and/or product brand?
Here are Review, Product and Category models:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Review extends Model {
protected $fillable = [];
public function product() {
return $this->belongsTo('App\Product');
}
public function user() {
return $this->belongsTo('App\User');
}
}
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model {
protected $fillable = [];
public function user() {
return $this->belongsTo('App\User');
}
public function reviews() {
return $this->hasMany('App\Review');
}
public function category() {
return $this->belongsTo('App\Category');
}
}
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model {
protected $fillable = [];
public function products() {
return $this->hasMany('App\Product');
}
}
If I use joins, then $review->user->id, $review->user->username, $review->id are not correct, I'm getting reviews.product_id as $review->id, and products.user_id as $review->user->id in blade template.
I was trying this join variant:
$reviews_query = Review::orderBy('reviews.id', 'DESC')
->Join('products', 'reviews.product_id', '=', 'products.id')
->Join('categories', 'products.category_id', '=', 'categories.id')
->Join('brands', 'products.brand_id', '=', 'brands.id')
->Join('users', 'reviews.user_id', '=', 'users.id')
->where('reviews.active', '=', 1)
->GroupBy('reviews.id')->paginate(5);
And this for filtering by category_id:
if (Input::has('category_id')){
$category_id = Input::get('category_id');
$reviews_query->where('categories.id', $category_id);
}
I'm not sure how to correctly address ambiguous ids such us product_id, review_id, user_id in blade template ($review->id, $review->user->id, all are messed up mutually)
Add hasManyThrough relationship in your category model
public function reviews() {
return $this->hasManyThrough('App\Review', 'App\Product');
}
now you can have all reviews by a category like this
$category->reviews();
you can add other query clauses to it like
$category->reviews()->orderBy('id', 'DESC')->paginate(5);
Try this,
$reviews = DB::table('review')
->join('product', 'product.id', '=', 'review.product_id')
->Join('category', 'product.category_id', '=', 'category.id')
->orderBy('id', 'DESC')->paginate(5);
for more you can visit-
Laravel join with 3 Tables
This may work for you.....
filter all reviews by a specific CategoryID then simply use ->
$categoryID = 1;
Categories::with(['products' => function($product){
$product->with('reviews');
}])->where('id', '=', $categoryID)->get();
also filter all reviews by a specific BrandID then
$brandID = 1;
Brands::with(['products' => function($product){
$product->with('reviews');
}])->where('id', '=', $brandID)->get();
I want to create join query for multiple models with separate (model) conditions.
I have create following query :
select * from studentinformation as s left join studentattandence a on s.id =
a.studentid where s.PersonalFirstName='Kathi' and s.PersonalLastName='irfan'
and s.Age='2' and s.gender ='Male' and s.StudentCourse='1' and
s.GuardianFirstName='test' and s.GuardianLastName = 'test' and a.date
BETWEEN '2015-02-01' AND '2015-02-07'
Table of studentinformation model name is "StudentAdmissionModel".
Table of studentattandence model name is "StudentAttandenceModel".
How can i do this laravel Eloquent ORM.
You would need to declare a relationship between the two in the StudentAdmissionModel, which would look something like this:
class StudentAdmissionModel extends Eloquent {
public function attendance()
{
// Assuming a one-to-one relationship
return $this->hasOne('StudentAttandenceModel','studentid');
}
}
Then you would be able to use the whereHas() function to query the relationship:
$admissions = StudentAdmissionModel::where('PersonalFirstName','=','Kathi')
->where('PersonalLastName','=','irfan')
->where('Age','=','2')
->where('gender','=','Male')
->where('StudentCourse','=','1')
->where('GuardianFirstName','=','test')
->where('GuardianLastName ','=','test')
->whereHas('attendance',function($q)
{
$q->whereBetween('date', array('2015-02-01','2015-02-07'));
})
->with('attendance') // Optional eager loading, but recommended
->get();
And you would be able to access the fields like this:
foreach( $admissions as $admission){
echo $admission->gender;
// or
echo $admission->attendance->date;
}