yii using/understanding the model correctly - php

I am looking to pull data for related products correctly with the YII framework, as Im only learning this framework Im wondering have you advice on how I should go about this.
Ive taken over a project which uses YII, and it seems to have already some functionality in place, but I dont know how to utilize it.
Here is my current code...
In my Product model I have written this myself..
public function getRelatedProducts($id)
{
$rawData=Yii::app()->db->createCommand('SELECT * FROM '.Product::model()->tableName().' as Product LEFT JOIN '.ProductRelated::model()->tableName().' as ProductRelated ON ProductRelated.related_id=Product.id LEFT JOIN '.Images::model()->tableName().' as image ON Product.image_id=image.id WHERE ProductRelated.product_id='.$id.' ')->queryAll();
return $rawData;
}
and I get this data using...
$related_products = Product::GetRelatedProducts($model->id);
This works but is not using the YII framework.
I have noticed there is a model called ProductRelated (with not much in it).But I am not sure how to use it.
This obviously refers to a table I have in the database called product_related, which has 2 fields, product_id and related_id, where related_id represents the id of the product to which it was assigned(related to).
I would like to use this class as it was obviously written with this in mind. Right now Im just skipping over it.
class ProductRelated extends BaseProductRelated {
public static function model($className=__CLASS__)
{
return parent::model($className);
}
public static function LoadByProductIdRelatedId($intProductId , $intRelatedId)
{
return ProductRelated::model()->findByAttributes(array('product_id'=>$intProductId,'related_id'=>$intRelatedId));
}
}
Having read up some more about YII I have rebuilt my query...
public function getRelatedProducts($id)
{
$criteria = new CDbCriteria();
$criteria->alias = 'Product';
$criteria->join ='LEFT JOIN '.ProductRelated::model()->tableName().' as ProductRelated ON ProductRelated.related_id=Product.id';
$criteria->join .=' LEFT JOIN '.Images::model()->tableName().' as image ON Product.image_id=image.id';
$criteria->condition = 'ProductRelated.product_id='.$id.'';
$criteria->order = 'Product.id DESC';
return $criteria;
}
However I am not sure what to do with that final bit of code and how to link it up with the model I have shown above.

there are many mistakes in your code. I don't think your predecessor known yii very well too.
There are included functionalities to get relations.
read about yii relational active records:
http://www.yiiframework.com/doc/guide/1.1/en/database.arr
Best regards

Yii model uses the ActiveRecord pattern. You'll find that the relations() method of your model allows you to define relational data.
If your Product.relations() method is properly set, then the related products shall be 'magically' available.
NB : I guess the relatedProduct table is an association table for a n-n relationship between Product and... Product so you will use MANY_MANY. Take a look at the sample code at the end of the documentation of the relations() method.

Related

Retrieve Parent Model Through Pivot Table Laravel

I'm currently struggling with retrieving data towards a parent model. I'll drop my database, classes, and things I've tried before.
I have 4 tables: sales_orders, products, work_orders, and product_sales_order (pivot table between sales_orders and products).
SalesOrder.php
class SalesOrder extends Model
{
public function products()
{
return $this->belongsToMany(Product::class)
->using(ProductSalesOrder::class)
->withPivot(['qty', 'price']);
}
}
ProductSalesOrder.php
class ProductSalesOrder extends Pivot
{
public function work_orders()
{
return $this->hasMany(WorkOrder::class);
}
public function getSubTotalAttribute()
{
return $this->qty* $this->price;
}
}
WorkOrder.php
class WorkOrder extends Model
{
public function product_sales_order()
{
return $this->belongsTo(ProductSalesOrder::class);
}
public function sales_order()
{
return $this->hasManyThrough(
ProductSalesOrder::class,
SalesOrder::class
);
}
}
So, what I want to retrieve sales order data from work order since both tables don't have direct relationship and have to go through pivot table and that is product sales order. I've tried hasOneThrough and hasManyThrough but it cast an error unknown column. I understand that error and not possible to use that eloquent function.
Is it possible to retrieve that sales order data using eloquent function from WorkOrder.php ?
You cannot achieve what you want using hasOneThrough as it goes from a table that has no ID related to the intermediate model.
In your example you are doing "the inverse" of hasOneThrough, as you are going from a model that has the ID of the intermediate model in itself, and the intermediate model has the ID of your final model. The documentation shows clearly that hasOneThrough is used exactly for the inverse.
So you still should be able to fix this, and use a normal relation as you have the sales_orders_id in your model SuratPerintahKerja, so you can use a normal relation like belongsTo to get just one SalesOrder and define it like this:
public function salesOrder()
{
return $this->belongsTo(SalesOrder::class, 'sale_orders_id');
}
If you want to get many SalesOrders (if that makes sense for your logic), then you should just run a simple query like:
public function salesOrders()
{
return $this->query()
->where('sale_orders_id', $this->sale_orders_id)
->get();
}
Have in mind that:
I have renamed your method from sales_order to salesOrder (follow camel case as that is the Laravel standard...).
I have renamed your method from sales_order to salesOrders for the second code as it will return more than 1, hence a collection, but the first one just works with one model at a time.
I see you use sale_orders_id, but it should be sales_order_id, have that in mind, because any relation will try to use sales_order_id instead of sale_orders_id, again, stick to the standards... (this is why the first code needs more parameters instead of just the model).
All pivot tables would still need to have id as primary and auto incremental, instead of having the id of each related model as primary... Because in SuratPerintahKerja you want to reference the pivot table ProdukSalesOrder but it has to use both produks_id (should have been produk_id singular) and sale_orders_id (should have been sales_order_id). So if you were able to use something like produk_sales_order_id, you could be able to have better references for relations.
You can see that I am using $this->query(), I am just doing this to only return a new query and not use anything it has as filters on itself. I you still want to use current filters (like where and stuff), remove ->query() and directly use the first where. If you also want to add ->where('produks_id', $this->produks_id) that is valid and doesn't matter the order. But if you do so, I am not sure if you would get just one result, so ->get() makes no sense, it should be ->first() and also the method's name should be salesOrder.
Sorry for this 6 tip/step, but super personal recommendation, always write code in English and do not write both languages at the same time like produks and sales orders, stick to one language, preferrably English as everyone will understand it out of the box. I had to translate some things so I can understand what is the purpose of each table.
If you have any questions or some of my code does not work, please tell me in the comments of this answer so I can help you work it out.
Edit:
After you have followed my steps and changed everything to English and modified the database, this is my new code:
First, edit ProductSalesOrder and add this method:
public function sales_order()
{
return $this->belongsTo(SalesOrder::class);
}
This will allow us to use relations of relations.
Then, have WorkOrder as my code:
public function sales_order()
{
return $this->query()->with('product_sales_order.sales_order')->first();
}
first should get you a ProductSalesOrder, but then you can access ->sales_order and that will be a model.
Remember that if any of this does not work, change all the names to camelCase instead of kebab_case.

One To Many Relationship in Eloquent with JSON field

I'm trying to create a connection between a JSON field in my database and a table which stores music by ID. So, I have a table called "playlists" which has a field called "songs". In this "songs" field I have a array[] of song ID's e.g. [1,2]. I tried the following code to make a relationship between these two tables:
class Playlist extends Model
{
protected $table = 'playlists';
public function songs()
{
return $this->hasMany('App\Music', 'id');
}
}
I used the foreign_key id because of the songs table which has a id field.
The code I used to retrieve the playlist from the controller is as follows:
$playlist = Playlist::find($id)->songs;
print_r($playlist);
Which outputs:
[1,2]
I most probably did something wrong, not understanding the relationships correctly. Could someone explain how this works? I looked up the documentation but did not get any wiser.
Laravel has no native support for JSON relationships.
I created a package for this: https://github.com/staudenmeir/eloquent-json-relations
If you rename the songs column to song_ids, you can define a many-to-many relationship like this:
class Playlist extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = [
'song_ids' => 'json',
];
public function songs()
{
return $this->belongsToJson('App\Music', 'song_ids');
}
}
class Music extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function playlists()
{
return $this->hasManyJson('App\Playlist', 'song_ids');
}
}
Although this is a very old post but I will go ahead and drop my own opinion for my future self and fellow googlers.....
So, If I got this question correctly, you are trying to use a JSON field for a relationship query. This issue I have stumbled across a couple of times, at different occasions for different use-cases. With the most recent being for the purpose of saving a couple of Ids belonging to different tables, in a single JSON field on a given table (While I keep pondering on why the Laravel guy won't just add this functionality already! I Know Pivots, Data Normalization etc....But I'm pleading for the 1%). Until I came across this post on Laracast that worked like a charm.
Apologies for the long intro, let me get right into it....
On your Playlist model (in Laravel 8.0 and a few older versions I can't really keep track of) you can do something like so;
public function songs()
{
$related = $this->hasMany(Song::class);
$related->setQuery(
Song::whereIn('id', $this->song_ids)->getQuery()
);
return $related;
}
I have the really good solution for keeping data in column on json format. It help me on previous project online shop
https://scotch.io/tutorials/working-with-json-in-mysql

ID lost despite left join

I am using a left join to combine two tables based on a common column but I seem to lose the primary key id in my left table. Every other column is returned
My controller:
$prod_manu = $request->prod_manu;
return $data = Product::leftJoin('manufacturers','products.Manufacturer_id','=','manufacturers.id')
->where('products.Manufacturer_id',$prod_manu)
->get();
Product Model:
class Product extends Model{
public function types(){
return $this->belongsTo('App\types','Type_id');
}
public function manufacturers(){
return $this->belongsTo('App\manufacturers','Manufacturer_id');
}
}
This is probably because both Product and manufacturers have an id column, so one of them gets overwritten.
Instead of using the left join, you can use eloquent's with method.
If you have your relationships and class structure setup properly you should be able to access the Manufacturer's products like this:
$manufacturer = Manufacturer::where('id', $prod_manu)->with('products')->get();
return $manufacturer->products;
Also note that class names should always be singular and start with a capital letter in laravel. App\manufacturers Shouldn't be valid. Please read the article I linked above about setting up your eloquent relationships.

more efficient way of working with YII? Querying the database

This is a difficult question to ask as the code I have is working fine. Im just learning the YII platform and my issue isnt that I cant get what I want working, but moreso if there is a better way of doing this that takes advantage of the YII platform and its classes.
Basically I have a webstore using a platform called Lightspeed which uses the YII platform.
In the product detail section I am looking to pull its related products. Thankfully Lightspeed has the tables in place for this already (which gives me more reason to think I am doing this wrong).
Right now what Im doing seems a little hard coded.
In my view I have this to get the products...
$related_products = Product::GetRelatedProducts();
I have nothing in my controller, and in my model I have this..
public function getRelatedProducts()
{
$rawData=Yii::app()->db->createCommand('SELECT * FROM xlsws_product as Product LEFT JOIN xlsws_product_related as ProductRelated ON ProductRelated.related_id=Product.id WHERE ProductRelated.related_id=Product.id ')->queryAll();
return $rawData;
}
As I said there is nothing wrong with this code, but I see so much functionality in place with all the other queries in the model that it makes me think Im doing this incorrectly.
Examples include..
protected function getSliderCriteria($autoadd=0)
{
$criteria = new CDbCriteria();
$criteria->distinct = true;
$criteria->alias = 'Product';
$criteria->join='LEFT JOIN '.ProductRelated::model()->tableName().' as ProductRelated ON ProductRelated.related_id=Product.id';
if (_xls_get_conf('INVENTORY_OUT_ALLOW_ADD',0)==Product::InventoryMakeDisappear)
$criteria->condition = 'ProductRelated.product_id=:id AND inventory_avail>0 AND web=1 AND autoadd='.$autoadd.' AND parent IS NULL';
else
$criteria->condition = 'ProductRelated.product_id=:id AND web=1 AND autoadd='.$autoadd.' AND parent IS NULL';
$criteria->params = array(':id'=>$this->id);
$criteria->limit = _xls_get_conf('MAX_PRODUCTS_IN_SLIDER',64);
$criteria->order = 'Product.id DESC';
return $criteria;
}
Thats just an example of a widget that seems to use this data (although Im unsure how that data turns into arrays, as when I print out $criteria I get arrays containing query commands.
Let me know if you need more clarification on what Im looking for.
You're right that you're not leveraging Yii. Yii (and other MVC frameworks), abstract the database layer out into a model.
The getSliderCriteria() you show above is an example of building criteria to refine the interactions with the model.
What you should try and figure out is what model represents the data you're looking for in Lightspeed, then building and applying the criteria to it.
In most modern frameworks, you shouldn't be writing much (if any) raw SQL . . .

find 'all' using INNER JOIN rather LEFT JOIN

I'm coding an app to sell cars, so i have a model called Anuncio(part that show the car, its in portuguese i dont know translate it) that has one Carmodel and Carmodel has one CarMake.
On anuncio controller i want load all Makes to populate combobox, so i'm using that:
$makes = $this->Anuncio->Carmodel->CarMake->find('list');
its working properly, but that find is bringing all makes, i want bring just makes that have at least one Carmodel recorded (something like INNER JOIN rather LEFT JOIN).
thanks
Hello you didn't provide any code so I won't give you exact answer but I will write how it can be done.
You should override the model's list method and specially list action
class Carmodel extends AppModel {
var $belongsTo = array('User');
function find($type, $queryData = array()) {
switch ($type) {
case 'list':
//do the query that joins tables in way you want
default:
return parent::find($type, $queryData);
}
}
}
More can be found on http://debuggable.com/posts/implementing-different-types-for-cakes-modelfind-method:485030de-4778-456e-8400-44d84834cda3
I hope that it helps you. You can create your own $type in this method you don't need to override list action but you can create your own or just create new method in Carmodel

Categories