Laravel eloquent model how to get data from relationship's table - php

I am developing a laravel application which has the following eloquent models
Product hasMany('App/Sku','products_id')
Sku belongTO('App/Product')
I have a controller 'ProductController' where the following code is available
public function index()
{
$products = Product::all();
foreach($products as $product){
$products_id = $product->products_id;
}
}
I am exposing RESTfull API which will allow my users to get all product details (including skus, shipping types etc..).
Suppose if I have an API GET : /products
The code which fetches all the product details will be some what the following
public function index()
{
$products = Product::all();
foreach($products as $product){
$products_id = $product->products_id;
$skus_data = Product::find($products_id)->skus;
}
// Now I have both the product details + skus which I can bundle into an array/json.
}
Now my question is , is this logic proper? In this case all the logics are in the controller since im using eloquent models I have a model for each table and the relationships are defined in it. Is there a way I can get all the details of a product/associated model (Products details (in table 1)+ Sku details (in table 2)) rather than using the below
foreach($products as $product){
$products_id = $product->products_id;
$skus_data = Product::find($products_id)->skus;
}
I am pretty new to laravel development and eloquent models. I will be using repository pattern for the development and in that case where does the aboe logic (Product+Sku combining) resides.
Please help out.

Yes, you can get the details of the products and skus without making one additional query per product using eager loading
( this is referred as the typical N+1 query problem where N is the number of the products )
Suppose the relation between your Product and Sku models model is:
Product
public function skus()
{
return hasMany('App/Sku','products_id');
}
To fetch the products data along with the sku data you can use the with method. In your controller:
Controller
$products = Product::with('skus')->get();
Then, in your views, you can get the info this way:
View
foreach ($products as $product)
{
//$product->skus is a collection of Sku models
dd( $product->skus );
}
For the repository question: if you want to use a repository you can place the eloquent-access part of your code inside the repository. So, for example you could have this method inside the repository:
ProductRepository
public function getProductsData()
{
//access eloquent from the repository
return Product::with('skus')->get();
}
then you can use your repository in your controller:
Controller
//inject the repository in the controller
public function __construct( ProductRepository $productRepo )
{
$this->productRepo = $productRepo;
}
//use the injected repository to get the data
public function index()
{
$products = this->productRepo->getProductsData();
}

If the repository pattern is used, do it like this.
public function index() {
$data = $this->passportRepository->with('user')->findWhere(['id'=>1]);
}

If I understand your question correctly, you can use eager loading.
public function index()
{
$products = Product::with('skus')->get();
}
This will give you an array of products that have a skus array in each product object.

You can try this:
public function index()
{
$products = Product::all();
foreach($products->skus as $product)
{
return $product;
}
}
This will give you the exact result in the object form.

Related

Laravel Multi BelongsTo RelationShip Merge with Eager Loading

Laravel version:7.0
reviews table (Model - Review) has id, product_type, product_id, rating columns.
product_type can be service, plugin, module and each value has own model App\Service, App\Plugin, App\Module. I could put model names directly in product_type but I prefer to use those values.
Here is Review model relationship.
public function plugin()
{
return $this->belongsTo(Plugin::class, "product_id")->withDefault();
}
public function module()
{
return $this->belongsTo(Module::class, "product_id")->withDefault();
}
public function service()
{
return $this->belongsTo(Service::class, "product_id")->withDefault();
}
public function getItem()
{
if($this->product_type=='module')
{
return $this->module;
}elseif($this->product_type=='service')
{
return $this->service;
}else {
return $this->plugin;
}
}
Now I want to get them with eager loading in Review Model as following:
$reviews = Review::with("getItem")->get();
Without Eager loading, I could use $review->getItem()->name // this returns name of product.
How can I get them with eager loading?
You could have implemented this easily as a polymorphic relationship. In your Reviews Model, you could do this:
Model Structure
App\Review.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Review extends Model
{
public function reviewable()
{
return $this->morphTo();
}
}
Then add reviews() method to your App\Service, App\Plugin and App\Module models
public function reviews()
{
return $this->morphMany('App\Review', 'reviewable');
}
Table Structure
You reviews table could look like this:
reviews
id - integer
body - text
reviewable_id - integer
reviewable_type - string
Note the reviewable_id and reviewable_type fields. The reviewable_id stores the id of the item reviewed and the reviewable_type stores the model related to the item.
Retrieving The Relationship
You may access the relationships via your models. For example, to access all of the reviews for a service, we can use the reviews dynamic property:
$service = App\Service::find(1);
foreach ($service->reviews as $review) {
//
}
You may also retrieve the owner of a polymorphic relation from the polymorphic model by accessing the name of the method that performs the call to morphTo. In your case, that is the reviewable method on the Review model. So, we will access that method as a dynamic property:
$review = App\Review::find(1);
$reviewable = $review->reviewable;
The reviewable will return the model on the Review model either Service, Plugin or Module

Laravel - Eloquent 3 Table Query

I know there are several questions out there on 3 table joins, but the examples are simpler than my set up.
I have three tables: Items, Attributes, Categories.
`item.item_code = attributes.item_code`
`attributes.category_id = category.id`
Using eloquent, I can access attributes no problem with:
$items = Item::with('attributes')->paginate(15);
But I can't seem to get the relationship set correctly to retrieve the category name.
With a standard MySql query I'd use something like:
SELECT category_name FROM items
JOIN attributes on items.item_code = attributes.item_code
JOIN categories on attributes.pg3_id = categories.id
WHERE items.item_code = 40992264
How do I achieve this using eloquent?
Edit - My bad - Totally messed up the SQL. Updated to reflect the correct table names and include the second join
Update
My models currently look like this:
class Attributes extends Model
{
public function category(){
return $this->belongsTo(Category::class);
}
}
class Product extends Model
{
public function item()
{
return $this->belongsTo(Item::class);
}
}
class Category extends Model
{
public function attributes()
{
return $this->belongsTo(Attributes::class);
}
}
But this still isn't returning a result. I've tried using
$items = Item::with('attributes.category')->get();
as suggested, but this still throws an error. If I update the Product model to:
class Product extends Model
{
public function item()
{
return $this->belongsTo(Item::class);
}
public function category(){
return $this->belongsTo(Category::class);
}
}
I don't get an error, but the relationship returns null.
You can do
$items = Item::with('attributes.category')->get();
So you access the category relationship inside the attributes relationship.
For example:
foreach ($items as $item) {
foreach ($item->attributes as $attribute) {
echo $attribute->category->id; // Will print the category id.
}
}

How to get list of all products in magento shop using an extended custom rest api

I have created an custom api in magento and currently facing the problem that i can get all the products that are available in the magento store, everytime i test the custom endpoint i get empty array. How can I get all the products that are available inside the magento store.
V1.php code
<?php
class Class_Restapi_Model_Api2_Restapi_Rest_Admin_V1 extends Class_Restapi_Model_Api2_Restapi
{
public function _retrieveCollection()
{
$product = Mage::getModel('catalog/product')->load(1);
return $product;
}
}
How can i get all products regardless of the category?
UPDATE
here is the updated code, now when I try to debug it in postman I get empty array
<?php
class Model_Restapi_Model_Api2_Restapi_Rest_Admin_V1 extends Model_Restapi_Model_Api2_Restapi
{
public function _retrieveCollection()
{
$products = Mage::getModel("catalog/product")->getResourceCollection()->load();
return $products->toArray();
}
}
the result from postman
[
[],
[]
]
your code is only loading a product model of first product.
Use a product collection to get all products
$collection = Mage::getModel('catalog/product')->getCollection();
$collection->load();
Beware this is a huge amount of data. Use Filters, Limits and other
methods to keep collection items as small as you need them

Method items cannot be found - Many to Many Relationship

I have a products and items table with a many to many relationship and a pivot table (item_product).
This is how my model looks like. When i try to retrieve items belonging to a product(category) $selectedItems= $products->items()->get(); i get an error "Method items cannot be found" . I can't seem to find where my error is. Please help
Product
public function items()
{
return $this->belongsToMany('App\Item','item_product','product_id','item_id')
->withTimestamps();
}
Item
public function products()
{
return $this->belongsToMany('App\Product','item_product','item_id','product_id')
->withTimestamps();
}
Controller
$products = Product::all()->where('id',1);
$myItems= $products->items()->get();
return view('products',compact('myItems','products'));
$products not an instance of Product
Try this:
$product = Product::findOrFail(1);
$myItems = $product->items;
return view('products',compact('myItems','product'));
Explanation:
$product = Product::findOrFail(1);
This will fetch a single product model by ID, or throw an exception if that product does not exist.
$myItems = $product->items;
This will set $myItems to a collection of items. This is equivalent to:
$myItems = $product->items()->get();
I recommend you read the documentation on relationships.

Laravel accessors appends to all results

I have a table field:
products
-title:New Product
-stock:9
-slug:new-product
And I want is get the additional data 'is_in_stock'. The result will be:
products
-title:New Product
-stock:9
-slug:new-product
-is_in_stock:true
for single data, I can modify the result on the controller itself, but I get stack when getting it to multiple data results. exp. Product::all();
I have read about Eloquent Mutators and Accessors. I have tried the logic on Model files. But I didn't know how to get the result.
This is my Product Model code:
class Product extends Model
{
protected $appends = ['is_in_stock'];
public function getIsInStockAttribute()
{
return $this->attributes['is_in_stock'] = false; // this will be boolean(true/false) result based on the current stock
}
}
Please help for any clue or reference for me to learn.
Thank you in advance :)
Finally I found the reference on http://laraveldaily.com/why-use-appends-with-accessors-in-eloquent/
So I change my Product Model Code to:
public function getIsInStockAttribute()
{
return ($this->stock > 0) ? true : false;
}

Categories