Consider a digital store
Definition:
Buyer->all buyer
Products->all products
Downloads->store those products that buyer bought
a Buyer can buy a Product and it store in Downloads, now I want to show to buyer list of downloads.
ProductController.php
public function buyerproducts()
{
$user = auth()->guard('buyer')->user();
if ($user) {
$files = Product::where('deleted', 0)
->where('deleted', 0)
->with('files', 'province:id,name', 'city:id,name')
->get();
// and here I got a loop.. to add some extra data
return response()->json(['data' => $files], 200);
} else {
return response()->json(['success' => 'no content'], 204);
}
}
Product.php
function files()
{
return $this->hasManyThrough('App\Download', 'App\Buyer', 'id', 'product_id', 'buyer_id', 'id');
}
But it return all data, not what buyer bought. any idea?
Note, that I have to get this data in product controller not download.
Products:
-----------------------
|id | name | seller_id |
-----------------------
| 1 | bmw | 1 |
-----------------------
| 2 | benz | 1 |
-----------------------
| 2 | reno | 2 |
-----------------------
Downloads:
------------------------------
|id | product_id | buyer_id |
------------------------------
| 1 | 1 | 1 |
------------------------------
| 2 | 1 | 2 |
------------------------------
| 3 | 2 | 22 |
------------------------------
Buyer:
------------------------------
|id | name | email |
------------------------------
| 1 | john | # |
------------------------------
| 2 | mike | # |
------------------------------
| 3 | dave | # |
------------------------------
The HasManyThrough relationship is expecting to go through 2 hasMany relationships, however, looking at your table definition, the first relationship is hasMany but the second would be a belongsTo. Since both keys relate to a single row in a different table (both belongsTo) we can instead create a belongsToMany relationship instead and treat the downloads table as a pivot.
There are a couple of different ways you can go about this.
Firstly, I would suggest setting up the relationships between Buyer and Product (is you haven't already done so):
Product
public function buyers()
{
return $this->belongsToMany(Buyer::class, 'downloads')->withTimestamps();
}
Buyer
public function products()
{
return $this->belongsToMany(Product::class, 'downloads')->withTimestamps();
}
Then in your controller method you could either keep the same query and use whereHas():
public function buyerproducts()
{
$user = auth()->guard('buyer')->user();
if ($user) {
$files = Product::where('deleted', 0)
->whereHas('buyers', function ($query) use ($user) {
$query->where('buyers.id', $user->id);
})
->with('files', 'province:id,name', 'city:id,name')
->get();
// and here I got a loop.. to add some extra data
return response()->json(['data' => $files], 200);
}
return response()->json(['success' => 'no content'], 204);
}
or alternatively you could not just query the products straight from the $user (buyer):
public function buyerproducts()
{
$user = auth()->guard('buyer')->user();
if ($user) {
$files = $user->products()->where('deleted', 0)
->whereHas('buyers', function ($query) use ($user) {
$query->where('buyers.id', $user->id);
})
->with('files', 'province:id,name', 'city:id,name')
->get();
// and here I got a loop.. to add some extra data
return response()->json(['data' => $files], 200);
}
return response()->json(['success' => 'no content'], 204);
}
Why don't you go with getting the products as Buyer object relationship.
So you define products in Buyer.php:
function products()
{
return $this->hasManyThrough('App\Download', 'App\Product', 'id', 'buyer_id', 'product_id', 'id');
}
And in the controller you can call it like:
$buyer->with([
'products.province:id,name',
'products.city:id,name'
])
->whereHas('products', function($query){
$query->where('deleted', 0)
})
->get()
then you can go with return response()->json(['data' => $buyer->products], 200);
Related
I have the following table structure:
Products
========
id | name
------|-------
1 | A
2 | B
Stocks
========
id | product_id | color_id | size | qty
------|------------|----------|----- |-----
1 | 1 | 1 | S | 37
2 | 1 | 1 | XL | 89
3 | 1 | 2 | S | 6
4 | 1 | 2 | L | 8
Colors
========
id | name | hex
------|-------|-------
1 | Red | #ff0000
2 | Green | #00ff00
What I want is to get the list of products with each of its available colors and sizes, so:
Product {
id: string
name: string
available_sizes: string[]
available_colors: Color[]
}
I looked up the Laravel documentation and figured that I need to use hasManyThrough (probably) to make a relation between Product <==> Color. But since my colors table doesn't have a stock_id, I am not understanding how can I connect them.
How can I achieve this?
As I understand, hasManyThrough works in a scenario that a product has many stocks and a stock has many colors. This is not the case.
You can simply eager load the relationship and present the data the way you want, using collection and higher order messages:
public function index()
{
$products = Product::with('stocks.color')->get();
return ProductResource::collection($products);
}
class ProductResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'available_sizes' => $this->stocks->map->size->unique()->toArray(),
'available_colors' => $this->stocks
->map->color
->map->name
->unique()
->toArray()
]
}
}
here you will use models
ProductModel
public function stocks()
{
return $this->belongsto(StockModel::class,'product_id');
}
StockModel
public function colors()
{
return $this->hasmany(ColorModel::class,'color_id');
}
Controller
$product=Product::with('stocks.colors')->find($id);
dd($product);
and check if the data are available.
or according to the documentation hasmanythrough
ProductModel:
public function colors(){
return $this->hasManyThrough(
Stock::class,
Color::class,
'product_id', // Foreign key on the product table...
'color_id', // Foreign key on the colors table...
'id', // Local key on the products table...
'id' // Local key on colors table...
);
}
Controller:
$product=Product::with('colors')->find($id);
| Data | DataChildren | Category
----------------------------------
| id | id | id
| name | data_id | name
| | category_id |
| | name |
Data Model:
public function data_children() {
return $this->hasMany(DataChildren::class, 'data_id', 'id');
}
DataChildren Model:
public function category() {
return $this->belongsTo(Category::class, 'category_id', 'id');
}
I want to get count of Category based on Data id through DataChildren.
I just want to take the Category records from Data so the result should be like this
name from category | Count of category for Data
-------------------------------------------------
Unpublished | 1
Published | 3
I've tried using this but return null
Data::withCount(['category'=> function($query){return $query->groupBy('category_id');}])->find(1);
you need to used many to many relationship
in Category Model:
public function datas()
{
return $this->belongsToMany(Data::class, 'data_childerens', 'category_id', 'data_id');
}
Then run this Query withCount :
Category::withCount('datas')->get();
Set Data Model:
public function categories()
{
return $this->belongsToMany(Category::class, 'data_childerens', 'data_id', 'data_id');
}
Then run this Query With and withCount :
Data::with('categories')->withCount('datas')->get();
I have three tables: products, categories and product_has_category
The categories table has a "type" field, which is either "gender" or "garment". So a product has many categories and a category has many products.
The trickier part comes in with how I have two different types of categories (i.e. gender and garment). A product can have only one "gender" category and only one "garment" category.
products table:
---------------
| id | style |
---------------
| 1 | Style 1|
---------------
| 2 | Style 2|
---------------
categories table:
----------------------------
| id | type | name |
----------------------------
| 1 | gender | men's |
----------------------------
| 2 | gender | women's |
----------------------------
| 3 | garment | crew neck |
----------------------------
| 4 | garment | v neck |
----------------------------
| 5 | garment | tank top |
----------------------------
product_has_category table:
----------------------------
| product_id | category_id |
----------------------------
| 1 | 1 |
----------------------------
| 1 | 3 |
----------------------------
| 2 | 2 |
----------------------------
| 2 | 5 |
----------------------------
So, with the above data, we have:
Style 1 is a men's crew neck, and Style 2 is a women's tank top.
I'd like to be able to retrieve products in this type of manner:
// returns Style 1, men's, crew neck
$product = Product::with(['gender', 'garment'])->find(1);
// returns Style 2, women's, tank top
$product = Product::with(['gender', 'garment'])->find(2);
I think I understand how I can set up a standard many-to-many relationship in my models using a belongsToMany() method setting the junction table as 'product_has_category'.
In my category model I have the following relationship:
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product', 'product_has_category', 'category_id', 'product_id');
}
}
But I'm not sure how to set the relationships in the product model to get the categories by a given category type. Here's what I have in my product model, which makes sense in a way, but laravel is throwing an error about the category.type being an unknown column.
class Product extends Model
{
public function gender()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')->where('type', '=', 'gender');
}
public function garment()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')->where('type', '=', 'garment');
}
}
Can anyone point me in the right direction for how to set up these types of data relationships?
I'm assuming your relationships work as intended.
Here's your problem:
public function gender()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('category.type', '=', 'gender'); // Here
}
public function garment()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('category.type', '=', 'garment'); // And here
}
When you chain query off of a relationship (in your case ->where('category.type'...), you're working on a related model's query. And as such you need to remove the category. part, since you're already working on a category query.
Like this:
public function gender()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('type', '=', 'gender'); // Removed 'category.'
}
public function garment()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('type', '=', 'garment'); // Removed 'category.'
}
Now if you call Product::with(['gender', 'garment'])->first() you will have these 2 categories separated.
I need to show feeds with total comments, and total likes on that feed with the detail of users who have commented.
Feeds Table
| id | movie_id | user_id | description |
|----|----------|----------|-------------|
| 1 | 1 | 1 | Lorem Ipsum |
Comments Table
| id | feed_id | user_id | comment |
|----|-------- |----------|-----------|
| 1 | 1 | 2 | comment 1 |
| 2 | 1 | 3 | comment 2 |
Likes Table
| id | feed_id | user_id |
|----|-------- |----------|
| 1 | 1 | 2 |
| 2 | 1 | 3 |
Users Table
| id | username| email |
|----|-------- |--------|
| 1 | a | a#a.com|
| 2 | b | b#b.com|
| 3 | c | c#c.com|
Relations
Feed.php
public function user () {
return $this->belongsTo('App\User');
}
public function likes () {
return $this->hasMany('App\Like');
}
public function comments () {
return $this->hasMany('App\Comment');
}
User.php
public function feeds () {
return $this->belongsToMany('App\Feed');
}
public function like () {
return $this->belongsTo('App\Like');
}
public function comment () {
return $this->belongsTo('App\Comment');
}
Like.php
public function user () {
return $this->belongsTo('App\User');
}
public function feed () {
return $this->belongsTo('App\Feed');
}
Comment.php
public function user () {
return $this->belongsTo('App\User');
}
public function feed () {
return $this->belongsTo('App\Feed');
}
Now I need to fetch all feeds (I have done with this), with comments count, likes count, and users details who have commented.
Ho can I get that in single query using Eloquent.
Try this
$commentsCount = \App\Models\Comment::select('feed_id',\DB::raw('count(id) as comments_count'))->groupBy('feed_id')->toSql();
$likesCount = \App\Models\Like::select('feed_id',\DB::raw('count(id) as likes_count'))->groupBy('feed_id')->toSql();
$records = \DB::table('feeds as f')
->leftJoin('comments as c','f.id','=','c.feed_id')
->leftJoin('users as u','c.user_id','=','u.id')
->leftJoin(\DB::raw('('.$commentsCount.') as k'),'f.id','=','k.feed_id')
->leftJoin(\DB::raw('('.$likesCount.') as l'),'f.id','=','l.feed_id')
->select('f.id as fid','f.description','u.id as uid','u.name','u.email','k.comments_count','l.likes_count')
->orderBy('fid')
->get();
$transform = function(array $records){
$records = collect($records)->groupBy('fid');
return $records->transform(function($items){
$feed['id'] = $items->first()->fid;
$feed['description'] = $items->first()->description;
$feed['count'] = [
'likes' => is_null($items->first()->likes_count) ? 0 : $items->first()->likes_count,
'comments' => is_null($items->first()->comments_count) ? 0 : $items->first()->comments_count,
];
$feed['users'] = $items->transform(function($user){
return is_null($user->uid) ? [] : ['id'=>$user->uid,'name'=>$user->name,'email'=>$user->email];
});
return $feed;
});
};
return array_values($transform($records)->toArray());
you can swap the closure function with other function. like
$this->transform($records);
You can simply access all those properties through the functions defined.
Example:
$feed = Feed::find($id);
foreach($feed->likes() as $like){
echo $like->user()->get('username');
}
And so forth. Up to a point when you call ->get(), you are accessing an object, which can traverse all of these.
I have these tables:
Entries table
---------------------------------
| id | blog_id | title |
---------------------------------
| 1 | 1 | 1st Entry |
---------------------------------
Blogs Table
-----------------
| id | name |
-----------------
| 1 | 1stBlog |
-----------------
Field Groups Table
-------------------------
| id | blog_id | name |
-------------------------
| 1 | 1 | Group1 |
-------------------------
Fields Table
---------------------------------
| id | field_group_id | name |
---------------------------------
| 1 | 1 | field_1 |
---------------------------------
Values Table
------------------------------------------
| id | field_id | entry_id | value |
------------------------------------------
| 1 | 1 | 1 | Hello World |
------------------------------------------
Now on my Models I've set up these relationships:
class Entry extends Model
{
public function blog()
{
return $this->belongsTo(Blog::class);
}
}
class Blog extends Model
{
public function entries()
{
return $this->hasMany(Entry::class);
}
public field_group()
{
return $this->hasOne(FieldGroup::class);
}
}
class FieldGroup extends Model
{
public function fields()
{
return $this->hasMany(Entry::class);
}
public function blog()
{
return $this->belongsTo(Blog::class);
}
}
class Field extends Model
{
public function group()
{
return $this->belongsTo(FieldGroup::class, 'field_group_id');
}
public function values()
{
// this method should get the values from the Values table per entry id
return $this->hasManyThrough(Value::class, Entry::class, 'id', 'entry_id');
}
}
class Value extends Model
{
public function field()
{
return $this->belongsTo(Field::class, 'field_id');
}
}
Using this query I can
$entry = Entry::with('blog.field_group.fields')->find(1)
I can get the entry, along with its blog, field groups and fields. I want to get the values associated with the entry too,
$entry = Entry::with('blog.field_group.fields.values')->find(1)
I am having trouble on which relationship to use. Any help is much appreciated. I just started using laravel.
Try it...
Replace ID by field_id:
return $this->hasManyThrough(Value::class, Entry::class, 'id', 'entry_id');
Like this:
return $this->hasManyThrough(Value::class, Entry::class, 'field_id', 'entry_id');
Because you are using the standard laravel column names you can simplify even more:
return $this->hasManyThrough(Value::class, Entry::class);
See in: https://laravel.com/docs/5.1/eloquent-relationships#has-many-through
I think you should use 'foreign_key' with 'hasMany' and 'hasOne'.
return $this->hasMany('Comment', 'foreign_key');
class Blog extends Model
{
public function entries()
{
return $this->hasMany(Entry::class, 'blog_id');
}
public field_group()
{
return $this->hasOne(FieldGroup::class, 'blog_id');
}
}
refer
http://laravel.com/docs/4.2/eloquent#one-to-many