Laravel : Polymorphic Relations + Accessor - laravel-5

I have a Gallery table that uses Polymorphic Relations so I can add Images and Videos to my gallery list.
Within the Gallery table I have a galleryable_type column that is populated with either App\Video or App\Image.
Is there a way for me to use an accessor (docs here) to change the value of galleryable_type to either video or image so I can use that column in JS to decide what gallery item type I'm dealing with?
I tried the following:
/**
* Get and convert the makeable type.
*
* #param string $value
* #return string
*/
public function getMakeableTypeAttribute($value)
{
return str_replace('app\\', '', strtolower($value));
}
But i end up with the following error:
FatalErrorException in Model.php line 838:
Class '' not found
I'm assuming that has to do with the accessor is being processed before the the polymorphic relationship but I'm not sure.
I can simply use the following in my controller:
foreach (Gallery::with('galleryable')->get() as &$gallery) {
$gallery->galleryable_type = str_replace('app\\', '', strtolower($gallery->galleryable_type ));
}
But that seems like a dodgy way of doing things. Could a Laravel guru shed some light on the best way to tackle this problem?
Thanks!

Well I've found an interesting way to solve this issue.
In your models (App\Video and App\Image) you have to add:
protected $morphClass = 'video'; // 'image' for image class
then in your register method in service provider class add:
$aliasLoader = \Illuminate\Foundation\AliasLoader::getInstance();
$aliasLoader->alias('video', \App\Video::class);
$aliasLoader->alias('image', \App\Image::class);
This will cause that you will write image, and video in galleryable_type in the database instead of class names.
So now you can easily get to this values with:
echo $model->galleryable_type;

Related

Laravel Model Define Parameters Won't Insert

I am currently moving over from symfony to laravel, it's quite a bit different when it comes to the database. So i have a basic model, i'm just going to use an example:
class Test extends Model
{
use HasFactory;
}
All good, i have a migration and the table created. However, i don't like this:
$test = new Test();
$test->my_field = 'hello';
$test->save();
I don't like it because it's having to use a magic __set() to create the parameter, if i define the parameter in my model like this:
class Test extends Model
{
use HasFactory;
public ?string $my_field;
}
I get database errors when it tries to insert when i define the params like this. Why is that? It's doing the same thing as __set() but i'm actually physically defining them, which in my opinion is a better way to code it as my IDE can typehint and it's just nicer to follow the program knowing what params are there.
What's the reason for it inserting when i don't define them, and not when i do? From my actual table which is bookings , has a field booking_ref:
General error: 1364 Field 'booking_ref' doesn't have a default value (SQL: insert into booking_reviews (updated_at, created_at) values (2021-12-13 14:13:08, 2021-12-13 14:13:08))
This happens when i define the $booking_ref param on the model, but if i take it out and rely on the __set() method it works fine. Doesn't make any sense to me right now.
I think this is a reasonable enough misunderstanding to be useful to future visitors, so I want to try to explain what's going on with some pseudo-code and some references to the current source code.
You are correct that when setting a property on a Laravel model, that is a column in the DB, internally Laravel is using the PHP magic method __set.
What this does is allow you to 1) set properties directly instead of calling some kind of setter function, and 2) interact with your table columns without needing the boilerplate of column definitions in your model.
Where the assumptions go wrong is with what __set is doing. __set does not have to simply set an actual property with the same name. __set is just a method you may implement to do whatever you want. What you assumption implies is that it's doing something like this:
public function __set($key, $value)
{
$this->{$key} = $value;
}
However, you can do whatever you want with the $key and $value passed to the magic method.
What Laravel does is call another method defined in the HasAttributes trait - setAttribute.
public function __set($key, $value)
{
$this->setAttribute($key, $value);
}
setAttribute does a few extra things, but most importantly it adds the key/value pair to Model property $this->attributes[].
To hopefully help this difference make sense, here is what the two __set methods would yield with a basic example:
$model->my_column = 'value';
// 1st example
/**
* {
* public $my_column = 'value';
* }
*/
// Laravel way
/**
* {
* protected $attributes= ['my_column => 'value'];
* }
*/
I won't go through both saving and updating since they're very similar, but to show how this is used, we can look at the save method, which calls performInsert and after a few more calls makes it's way back to the attributes property to determine what to actually insert into the query.
Summary
Laravel does not use custom model properties when deciding what column/values to add to queries.
This is why when you create custom mutators, you interact with the attributes property just like Laravel does internally.
Anytime you introduce "magic" into code, you have some tradeoffs. In this case, that tradeoff is slightly less clarity with what database columns are actually available. However, like I mentioned in comments, there are other solutions to make models more IDE friendly like Laravel IDE helper.

Laravel - Model get data from another Model

I'm quite new to laravel and I'm trying to understand the Eloquent Relations.
I've already read some answers and the documentation but I haven't found a simple case similar to mine.
I have two model with one-to-many relation.
Document Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Document extends Model
{
public function dossiers()
{
return $this->belongsTo('App\Dossier');
}
protected $table = 'documents';
protected $primaryKey = 'id_document';
}
Dossier Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Dossier extends Model
{
public function documents()
{
return $this->hasMany('App\Document');
}
protected $table = 'dossiers';
protected $primaryKey = 'id_dossier';
}
So there is an attribute "protocol" inside both table. Inside Dossier is an unique attribute, inside Document can be repeated because more documents may be part of same dossier with that protocol.
Let's suppose I have to create a Document Model. But I need an attribute "color" that already exists in a Dossier.
so:
I have a form for the document. That can set all attribute except
color (so it can create the protocol shared with Dossier)
I submit the form then check if exists a Dossier with that protocol
(that is not the primary_key)
If i find a Dossier with that protocol, I took the Color of that dossier and
I insert it in the Document Model.
I'd like to know how I should implement the second step.
At the moment I've wrote in the DocumentController:
$document = Dossier::where('protocol', '=', $request->protocol)->first();
and then
$document -> color;
But I fell that's not the way.
Thanks for any advice
I think you want to insert color in document from dossier table if protocol match. If i understand you correctly then you can write it like this.
$dossier = Dossier::where('protocol', '=', $request->protocol)->first();
$color = ($dossier) ? $dossier->color: 'defaultColor';
Now in your create document
$document -> create([
'color' => $color,
... other form data here
]);
Note: it is not good practice to use primary key field as id_document or id_dossier. Better you change these to id, otherwise your current relationship will not work. Default, laravel relationship assume id as primary key. If you want to use different primary key name then you need to pass that key name in relationship as a second parameter.
$this->hasMany('App\Comment', 'foreign_key', 'local_key');
https://laravel.com/docs/5.6/eloquent-relationships#one-to-many
There are manyway to write the relation code, one is as rkj said anad u did as well.
The other one which i personally prefer is
$this->hasManny(Comment::class,'foreign_key', 'local_key'); //This is only if you are not following laravel standard table syntex.
But if your local key is id and foreign key on another table is comment_id. Then you dont need to add foreign key and primary key. You can simply do like this
$this->hasManny(Comment::class);
For making query, You can simply do this
$dossier = Dossier::where('protocol', $request->protocol)->first(); //This will give object
You do not need = , if its other then that u need to add that. This way its looks much cleaner right?
Then to display you can simply do this $dossier->color

Update Table Using Laravel Model

I've got a table for a sports team. The record shows the team selection and some other information. I want to update the record with the team selection. My model is thus:
class Selection extends Model {
protected $table = "selection";
protected $fillable = [
'loose',
'hooker',
'tight',
'secrow1',
'secrow2',
'blindflank',
'openflank',
'eight',
'scrum',
'fly',
'leftwing',
'rightwing',
'fullback',
'sub1',
'sub2',
'sub3',
'sub4',
'sub5'
];
}
So I have a form which gives all the data for the positions and gives the id for the record in the DB. In my controller, I've got:
public function storeFirstTeam()
{
$input = Request::all();
Selection::update($input->id,$input);
return redirect('first-team');
}
But I get the following error:
Non-static method Illuminate\Database\Eloquent\Model::update() should not be called statically, assuming $this from incompatible context
Can anyone point out my silly error?
Please check the code below and this would solve your problem:
Selection::whereId($id)->update($request->all());
The error message tells you everything you know: you’re trying to call a method statically (using the double colons) that isn’t meant to be.
The update() method is meant to be called on a model instance, so first you need to retrieve one:
$selection = Selection::find($id);
You can then can the update() method on that:
$selection->update($request->all());
You should write it like given example below:
Selection::where('id', $input['id'])->update($input);
// Or use this using dynamic where
Selection::whereId($input['id'])->update($input);
Alternatively, you may write it like this as well:
Selection::find($input['id'])->fill($input)->save();
You can also simply update the fields manually:
Selection::whereId($id)->update($request->all());
it is possible to update with primary key but in my case I dont have id field in the detail table. To do it just run a query like this:
DB::table("shop_menu_detail")
->where(['name' => 'old name', 'language' => 'english'])
->update(['name' => 'new name']);
where is used as it is.

How to specify a class name for a generated Datatable

I am using Chumper's Datatable package for Laravel, and want to add a class name to the generated table. I tried passing it via setOptions():
$table = Datatable::table()
->addColumn($columns)
->setUrl(route('admin.reports.top_recruiters.datatable'))
->setOptions(array(
'bProcessing' => true,
'class' = 'table-striped'
))
->noScript();
echo $table->render();
However, that method appears to only pass options that are specific to the Datatables JavaScript.
The documentation does not appear to mention the way. What is the right way to apply custom classes to the generated table?
A little source diving revealed an undocumented method in the Table class called setClass().
public function setClass($class)
{
$this->className = $class;
return $this;
}
The class name can be applied to the table instance like this:
$table = Datatable::table()
->setClass('<your class name>')

How to get position value from a model (catalog/product_image) in Magento?

I only have this Magento model (catalog/product_image). Is there any way for me to get position value or label value of that image. When I var dump this model, I can only get width, height, quality, _keepAspectRatio, etc. The most valuable data I can get from it is image file name like this: /s/e/productABC.png
Im not quite sure if you will get anything more useful from the catalog/product_image model. From your question it sounds like you are wanting to fetch the image label. If you have access to a product object you can use:
$_product->getMediaGalleryImages()->getItemByColumnValue('label', 'LABEL_NAME')->getUrl();
This will return you the label of the image.
If you need any more help please share what objects you have access to and what you are trying to achieve.
in that class there is just one property and one set and one get function for file
Property : protected $_baseFile;
Function
/**
* Set filenames for base file and new file
*
* #param string $file
* #return Mage_Catalog_Model_Product_Image
*/
public function setBaseFile($file)
and
public function getBaseFile()
{
return $this->_baseFile;
}
This implies: You will not get position using this object.
Just check getBaseFile that will return a string.
I always suggest that for any given object if extended from varien_object use $object->debug(); as this provides lot of information on what all it stores.

Resources