Laravel - Single Model for Multiple Tables - php

I want to use a single Model file for multiple tables.
Why???
The Table structure of all the tables is same
I have few columns to be stored as JSON Arrays and I would like to use Laravel's built in Json Serialization rather than manually serializing Arrays.
I have already read on laracast blog that it's not possible in Laravel but is there any other way to make it possible.
Thanks in advance!!!

You can just create a base model that has the logic that is common to all the models, and then create your individual models that inherit from the base model.
class Auto extends Model
{
protected $casts = [
'details' => 'json',
];
public function getWheelsAttribute()
{
return $this->details->wheels;
}
}
class Car extends Auto
{
// models your "cars" table
}
class Truck extends Auto
{
// models your "trucks" table
}
class Bus extends Auto
{
// models your "buses" table
}
Or, you could create a trait with the common functionality and use the trait in all your child models.
trait HasJsonDetails
{
protected $casts = [
'details' => 'json',
];
public function getWheelsAttribute()
{
return $this->details->wheels;
}
}
class Car extends Model
{
// models your "cars" table
use HasJsonDetails;
}
class Truck extends Model
{
// models your "trucks" table
use HasJsonDetails;
}
class Bus extends Model
{
// models your "buses" table
use HasJsonDetails;
}
Or, another option, if the table structure truly is and will always be the same, would be to combine all your tables into one table and use single table inheritance to have multiple models all use the same table.
With this method, you would add a type field to your table to tell you which class to use to model the individual row. It also requires some customization, but you can find an STI package to use, or follow this forum thread for more information:
https://laravel.io/forum/02-17-2014-eloquent-single-table-inheritance
This, of course, would still need to be combined with one of the methods mentioned above to share implementation logic across the multiple models.

Related

How can I mock a different model to retrieve a polymorphic relationship?

I have 3 data models, one of which extends the other:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Opinion extends Model
{
public function reactions()
{
return $this->morphMany('App\Models\Reaction', 'reactable');
}
...
}
namespace App\Models\Activity;
use App\Models\Opinion;
class ActivityOpinion extends Opinion
{
...
}
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Reaction extends Model
{
public function reactable()
{
return $this->morphTo();
}
...
}
The App\Models\Opinion model has a polymorphic relationship with the App\Models\Reaction model. I can retrieve all of the App\Models\Opinion reactions no problem, so I know the relationship works great.
My question is, how can I retrieve the same set of reactions from the App\Models\Activity\ActivityOpinion model? Because right now, it is looking for App\Models\Activity\ActivityOpinion as the relationship but I need it to look for App\Models\Opinion. Is it possible to mock another model in a polymorphic relationship?
This is because in a Polymorphic Relationship in the stored data (if leaved as default) the relationship type gets the class namespace (sort of) to specify wich model needs to be returned. That's why when you try to access to your reactions() relationship from ActivityOpinion it will look up for the App\ActivityOpinion value in the reactable_type.
You can customize the morph class to search in the model addind this:
Opinion.php
protected $morphClass = 'reaction';
This should be enough, if not, add it also in the ActivityOpinion model.
Note
This could breake some things when trying to search results using Eloquent. Check this other answer in order to address this possible inconviniance.
Update
I've just found out that you could do all this even easier with MorphMap. From the docs:
Custom Polymorphic Types
By default, Laravel will use the fully qualified class name to store
the type of the related model. For instance, given the one-to-many
example above where a Comment may belong to a Post or a Video,
the default commentable_type would be either App\Post or
App\Video, respectively. However, you may wish to decouple your
database from your application's internal structure. In that case, you
may define a "morph map" to instruct Eloquent to use a custom name for
each model instead of the class name:
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'posts' => 'App\Post',
'videos' => 'App\Video',
]);
You may register the morphMap in the boot function of your
AppServiceProvider or create a separate service provider if you
wish.

Laravel on some kind of Model Ready method

Well i don't know how to format the title of this post in very clear way, but here's my question:
Say i have
Posts::find('1);
Photos:find('1');
... and so on, every mode db request
now by default i can access db columns, for instance the id: through model->id
$Photos = Photos::find('1')->first();
echo $Photos->id; // will return 1
what i want is that i need all those kind of requests to add a custom field automatically like hashed_id, which is not in the database, which in return will make all models have a hashed_id as well, i know i can add that field to database and then grab it but i need it for different reasons/implementations
i did create a BaseModel and every Model will extend that BaseModel, so Photos extends BaseModel, BaseModel extends Model... and all that etc etc.
but i need some kind of constructor, upon retrieving data to process the data automatically without having to add -let's say- a hash_id() after retrieving the data.
something like, onAfterGet(), onReady()....sort of commands.
i hope my question is clear.
Thanks.
What you're looking for is an Accessor. Accesors can be used to add custom attributes to the model. Combine this with the $appends property and you have exactly what you need. The $appends property adds the custom accessor in every result.
You can do this by creating a base model like you've stated in the question or by using traits. I'll show you an example on how to achieve this using a base model.
Let's create base model called BaseModel. All other models that need this custom attribute will extend this.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BaseModel extends Model
{
protected $appends = ['hashed_id'];
public function getHashedIdAttribute()
{
return some_hash_function($this->id);
}
}
We have a Image model which extends our BaseModel.
<?php
namespace App;
class Image extends BaseModel
{
}
Now every result from the Image model will have the hashed_id field added by default.
Accesor documenation https://laravel.com/docs/5.4/eloquent-mutators#defining-an-accessor
If I understand you right, all you need to do is to define mutator, for example:
<?php
class Photo extends Model
{
/* ... model implementation ... */
public function getHashedIdAttribute()
{
return md5($this->id);
}
}
Then you can access property like it was in database:
echo Photo::find(5)->hashed_id;

Laravel 5.1 - Dry on same characteristics models

In my Laravel 5.1 App I have a lot of aux Models with the same structure. I was thinking in the posibility of make one model and controller for using all of them, but I cannot figure how to do.
I explain, all the database aux tables have the fields ID and name, and are made for CRUD operations and for filling the forms all over the App.
Is possible to specify the table on the methods implemented by Laravel? I mean, stablish the table on construct, on get(), etc. This would made the work a much more simple if I could do AuxTable::create("sex") or even in requests like $request->auxtable("studies")->get().
Am I explaining?
you can do it with single model like below in Model class there is a method called setTable($table) which can set the table name you want to use so consider below
use Illuminate\Database\Eloquent\Model;
class AuxTable implements Model {
//other class properties
}
in your controller use the model like below
class SampleController extends BaseController {
public function index() {
$model = new AuxTable;
$model->setTable('sex');
$model->get();
}
}
this should do the trick

Polymorphically find model from database in Yii2

I have one table in the database(mysql). But this table stores several slightly different types of rows. The type depends on this tables's type column. I have an abstract ActiveRecord class for a table and several descendant subclasses implementing slightly different logic for the rows of the same table of different types. Now I am implementing an update controller action for all the types of rows. I am provided with an id of the row and need to create an ActiveRecord instance representing the row with this id. But I somehow need to create instances of different subclasses depending on the type of the corresponding row.
If I were provided with both a type and an id I could've used a factory to pick a corresponding subclass. But I can already have the type in the database and an id gives me enough information to pick it from there. But if I were to pick the type from the database first and then to create an instance of the corresponding subclass that would've meant executing the same query twice.
I want to find a good way to get the data from the database and then pick a right ActiveRecord subclass to create an instance for it without making excessive queries or requiring excessive data. Is there a way to do it Yii2?
Or should I approach this problem somehow differently? The actual problem is having several almost the same but a bit different entities stored in a single table with a bit different business-logic.
One of approaches to this problem is called "Single table inheritance" and described by Martin Fowler here. There is also good article about its implementation in Yii 2 in samdark's (one of the main Yii 2 contributors) cookbook, which is currently in process of writing but is available on Github.
I'm not going to copy the whole article, but leaving just link is also not enough. Here are some important things:
1) Create common query for all types of objects (for example cars):
namespace app\models;
use yii\db\ActiveQuery;
class CarQuery extends ActiveQuery {
public $type;
public function prepare($builder)
{
if ($this->type !== null) {
$this->andWhere(['type' => $this->type]);
}
return parent::prepare($builder);
}
}
2) Create separate model for each type (extending from common model Car):
Sport cars:
namespace app\models;
class SportCar extends Car
{
const TYPE = 'sport';
public static function find()
{
return new CarQuery(get_called_class(), ['type' => self::TYPE]);
}
public function beforeSave($insert)
{
$this->type = self::TYPE;
return parent::beforeSave($insert);
}
}
Heavy cars:
namespace app\models;
class HeavyCar extends Car
{
const TYPE = 'heavy';
public static function find()
{
return new CarQuery(get_called_class(), ['type' => self::TYPE]);
}
public function beforeSave($insert)
{
$this->type = self::TYPE;
return parent::beforeSave($insert);
}
}
3) Override instantiate() method in Car model to return correct type of cars:
public static function instantiate($row)
{
switch ($row['type']) {
case SportCar::TYPE:
return new SportCar();
case HeavyCar::TYPE:
return new HeavyCar();
default:
return new self;
}
}
Then you can use any type of cars individually as regular models.

laravel many to many relationship in this case

I have already checked this official example http://laravel.com/docs/eloquent#many-to-many-polymorphic-relations
but I still confused because I may have a different case.
I have a DetailsAttribute model which deals with details_attribute table.
I have a Action model witch deals with action table.
The relationship between them is many to many.
So I created a new table details_attribute_action with model DetailsAttributeAction
My DetailsAttribute model should have:
public function actions(){}
My Actions model should have:
public function detailsAttributes(){}
and my DetailsAttributeAction model should have functions but I don't know what they are.
My question is what is the code inside the previous functions please? and should really the DetailsAttributeAction have functions of not?
What you're looking for is a Many-to-Many relation, not one that is polymorphic.
http://laravel.com/docs/eloquent#many-to-many
Your code should look something like this:
class DetailsAttribute extends Eloquent {
// ...
public function actions()
{
// Replace action_id and details_attribute_id with the proper
// column names in the details_attribute_action table
return $this->belongsToMany('Action', 'details_attribute_action', 'details_attribute_id', 'action_id');
}
}
class Action extends Eloquent {
// ...
public function detailsAttributes()
{
return $this->belongsToMany('DetailsAttribute', 'details_attribute_action', 'action_id', 'details_attribute_id');
}
}
You won't have to worry about how to create the DetailsAttributeAction model in Laravel. It's simply a table to map the Many-to-Many relationships you've created.

Categories