Laravel Inheritance - Auto instantiate by database field - php

I have the following classes:
abstract class File extends Eloquent {
protected $table = 'files';
}
class LocalFile extends File {
}
class RemoteFile extends File {
}
I only have files table, LocalFile or RemoteFile table doesn't exists. files table has a type field with has the either App\LocalFile or App\RemoteFile stored.
Is there a way to auto cast the file's subclass during a find?
Note:
I have other models implemented with morph, but I can't see use of morph here.

Sounds like you're after global scopes. These allow you to apply a scope to a model for every query that is done using the model. https://laravel.com/docs/5.2/eloquent#global-scopes
You could apply a global scope to both of your models so that RemoteFile only looks at type === 'App\RemoteFile' and vice-versa for the LocalFile model

Related

Get model table name without query

I have a package media library by spatie. I need to get table name of the model.
I know that I can do this:
public function getPath(Media $media) {
$name = (new $media->model())->getTable()
}
But this creates a new query. I don't need to create an extra query on database. In table media, I have a column a model_type, where records can be like this: App\ModelName. Maybe I can get names of the model without a query?
There is an answer in laravel framework github:
https://github.com/laravel/framework/issues/1436 .
So it seems you will need to extend Media model.
Example from github
class BaseModel extends Eloquent {
public static function getTableName()
{
return with(new static)->getTable();
}
}
class User extends BaseModel {
}
User::getTableName();
I don't think "new model()" created a query on the database, it just spawns a new object instance of the model class. I don't know the library by heart, but given that it's a Spatie library, it probably functions very similar like Eloquent does, which has the same behaviour.

Laravel - Single Model for Multiple Tables

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.

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;

How to share scopes between classes in Laravel 5

I have several Laravel models which have the same functionallity.
I'm trying to implement some sort of ::All() functionallity but with another logic behind it.
For example: all my models have an "Active" boolean flag, which means that I get all of my languages like: $language = Language::where('active', 1)->orderBy('name')->get();. The same goes for hobbies, semesters, etc.
I'm trying to do something like this in my base_model from which all other models extend:
public static function getActive()
{
return this::where('active', 1)->orderBy('name')->get();
}
this would save me lots and lots of redundant code, but as a newbie I'm struggling with the code.
How can I dynamically define the Model I want to retrieve?
Any ideas?
You can use Laravel query scopes for this. For example:
//your base model
class BaseModel extends Model
{
//every class inheriting from this will have this scope
public function scopeActive($query)
{
return $query->where('active', 1)->orderBy('name')->get();
}
}
//your child models will inherit the scope from the parent class
class Language extends BaseModel
{
//your model's methods
}
//use the scope to get all the active languages
$languages = Language::active();

Yii: add prefix/postfix to model class naming

Is there any way I can get Yii to work with models that have a prefix or postfix in their class name?
For example, I have a table user, which corresponds to the model User. Now, I want this model to have a prefix, say, EmulatedUser. Is there any way to achieve that without renaming my table?
The table and class name don't have to be the same. You can override the tableName in your model:
<?php
class EmulatedUser extends CActiveRecord {
public function tableName() {
return 'user';
}
}

Categories