jonspalmer / single-table-inheritance Goto Github PK
View Code? Open in Web Editor NEWA Single Table Inheritance Trait for Eloquent/Laravel
License: MIT License
A Single Table Inheritance Trait for Eloquent/Laravel
License: MIT License
can we set the type of model using the base model.
this one is failing to save unless i define a
protected static $singleTableType = 'test';
in the base model.
what will this do is it will resolve to that type of model when retreive again.
i want to avoid using if else statement to instantiate the child class.
class Base extends Model {
use SingleTableInheritanceTrait;
protected static $singleTableTypeField = 'type';
protected static $singleTableSubclasses = [
Child::class,
];
protected $fillable = [
'name',
'description',
'type'
];
}
....
$base= new Base();
$base->name = 'sample name';
$base->description = 'sample description';
$base->type = 'child'; // this one
$base->save();
Hello, I think I have discovered a bug.
// WORKS
$factory->define(Car::class, function (Faker $faker) {
return [
'type' => 'car',
];
});
// DOES NOT WORK
$factory->define(Car::class, function (Faker $faker) {
return [];
});
I would expect that since the factory is defined for the concrete type that the type can be inferred that way. The same way that I do not need to specify the type when using the class constructor or create()
method.
// WORKS
$car = new Car();
$car->save();
// WORKS
Car::create();
This package doesn't work for Laravel 6.0 because it uses the starts_with
helper, which has been removed from the framework.
I'm using STI in Laravel with mysql on a businesses model so it has a type Business, then other types extend it (Hotel, Restaurant, etc). I can create new businesses of any type just fine using STI models (eg. Hotel::create(['name' => 'test'])). I can also fetch them (eg. Business::all() or Hotel::all()). However, I cannot update them.
For example, I narrowed the code down to a very basic update or save command on a Hotel and neither of these work:
public function update()
{
$business = Business::find(1);
return $business->update(['name' => 'lol']);
}
OR
public function update()
{
$business = Hotel::find(1);
return $business->update(['name' => 'lol']);
}
They both will return true, but no change is reflected in the database. If I remove the 'use SingleTableInheritanceTrait;' from my model, the update works correctly in both cases above - the database reflects the change.
Here are the models. I have $throwInvalidAttributeExceptions set to true just in case.
Business.php
<?php namespace Directory\Businesses;
use Cviebrock\EloquentSluggable\SluggableInterface;
use Cviebrock\EloquentSluggable\SluggableTrait;
use Eloquent;
use Nanigans\SingleTableInheritance\SingleTableInheritanceTrait;
class Business extends Eloquent implements SluggableInterface{
use SluggableTrait;
use SingleTableInheritanceTrait; // <---- removing this makes it work
protected $table = "businesses";
protected $fillable = ['id', 'name', 'category', 'slug'];
protected static $persisted = ['name', 'category', 'slug'];
protected static $throwInvalidAttributeExceptions = true;
/**
* Columns for sluggable business name
*
* @var array
*/
protected $sluggable = array(
'build_from' => 'name',
'save_to' => 'slug',
);
/**
* Table column for subclass name
*
* @var string
*/
protected static $singleTableTypeField = 'category';
/**
* Subclass models
*
* @var array
*/
protected static $singleTableSubclasses = ['Directory\Businesses\Hotel', 'Directory\Businesses\Restaurant', 'Directory\Businesses\Retail'
];
}
Hotel.php
<?php
namespace Directory\Businesses;
class Hotel extends Business {
/**
* Subclass identifier in db table
*
* @var string
*/
protected static $singleTableType = 'hotels';
protected static $persisted = ['hotel_capacity', 'hotel_type_id'];
}
Any idea what may be going on? Thanks!
I noticed that your child class map is a simple array. Unfortunately, this would break if, at a later date, I changed the namespace or class name of one of the children. It would be nice to optionally use a simple hashed class map. I could throw in some code & submit a pull request if you'd like.
Hello,
I have a table with no limitation for duplicate data. But every trying to add new entry, it always throw SingleTableInheritanceException.
Here is my table schema:
CREATE TABLE `base_posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`content` text COLLATE utf8_unicode_ci NOT NULL,
`excerpt` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`post_type` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `base_posts_post_type_index` (`post_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Models:
class BasePost extends Model
{
use SingleTableInheritanceTrait;
protected $table = 'base_posts';
protected $fillable = ['title', 'content', 'excerpt', 'slug'];
protected static $singleTableTypeField = 'post_type';
protected static $singleTableSubclasses = [Story::class];
}
class Story extends BasePost
{
protected static $singleTableType = 'story';
}
Sample code:
$ ./artisan tinker
Psy Shell v0.7.2 (PHP 5.6.24-1+deb.sury.org~trusty+1 โ cli) by Justin Hileman
>>> App\Models\Story::create(['title' => 'title', 'content' => 'content']);
=> App\Models\Story {#667
title: "title",
content: "content",
slug: "title",
post_type: "story",
updated_at: "2016-08-12 03:59:29",
created_at: "2016-08-12 03:59:29",
id: 1,
}
>>> App\Models\Story::create(['title' => 'title', 'content' => 'content']);
Nanigans\SingleTableInheritance\Exceptions\SingleTableInheritanceException with message 'Cannot construct newFromBuilder without a value for post_type'
>>>
Using Laravel 5.2.43 and nanigans/single-table-inheritance 0.6.1.
Hey,
Thanks for the great package.
Since v.0.8.6 we are encountering some strange behaviour with getAttribute
.
It seems $this->setRawAttributes($filteredAttributes, true);
call in nanigans/single-table-inheritance/src/SingleTableInheritanceTrait.php:226
now only sets the pivot attributes instead of all attributes.
As a result, all normal attributes of the model (not loaded via pivot) are unavailable
I'm not fully understanding the full scope of the change, but shouldn't $filteredAttributes = array_intersect_key($attributes, array_flip($persistedAttributes));
be $filteredAttributes = array_diff_key($attributes, array_flip($persistedAttributes));
?
For now, I'll redefine
protected function getPivotAttributeNames($attributes)
{
return [];
}
to fix it on our end, but I suppose It should be fixed here as well
Regards,
Levi
Hi,
I wanna make $singleTableSubclasses
configurable (i.e. store this under config and read it as something like $singleTableSubclasses = config('vehicle.subclasses');
. Right now it's a static variable so this is not possible. Is there a quick way to achieve this without rewriting too much of your implementation?
Cheers
PHP hangs when parent and child inheritance is not setted properly.
For example, the class MiniCar
is not proper inheritance. But it never emits error, just hang PHP which makes hard to debug.
use Nanigans\SingleTableInheritance\SingleTableInheritanceTrait;
class Vehicle extends Model
{
use SingleTableInheritanceTrait;
protected $table = "vehicles";
protected static $singleTableTypeField = 'type';
protected static $singleTableSubclasses = [Car::class];
}
class Car extends Vehicle
{
protected static $singleTableType = 'car';
protected static $singleTableSubclasses = [MiniCar::class];
}
// This is wrong, but it never emits error.
class MiniCar extends Vehicle
{
protected static $singleTableType = 'minicar';
}
// This will lead you to infinite execution of PHP script.
Vehicle::getSingleTableTypeMap();
Is there a way to emit errors in this situation?
hi
I just found this library when i was trying to implement something similar. Great stuff, thanks.
In fact, I am new to laravel and eloquent.
As I am using mongodb , i also found this mongodb version eloquent https://github.com/jenssegers/laravel-mongodb
However, when I try to use SingleTableInheritanceTrait to that 'Moloquent' I have to make two changes in order to make it work:
1.at line 171 of SingleTableInheritanceTrait.php
$classType = isset($attributes->$typeField) ? $attributes->$typeField : null;
to
$classType = isset($attributes[$typeField]) ? $attributes[$typeField] : null;
this one i guess is something related to 'mongo' as it will return pure array, i didn't have time to check with 'mysql'
2.at line 195 of SingleTableInheritanceTrait.php
return $this->getTable() . '.' . static::$singleTableTypeField;
to
return static::$singleTableTypeField;
same thing here as the query column key needs to be the field name itself without 'table' name
Any thought about this?
Could you please include a note about how querying works?
Do query builders return instances of the Child classes, or the table class?
For example, if I have Car and Truck extending Vehicle, and I query the vehicles table, do I get back a 2 instance of Vehicle, or 1 Car and 1 Truck?
Is there anything special we need to do when handling this?
Not knowing the answer to this is making me not want to try this package... but I NEED this package.
Laravel 5.4 is ready to go live next week. Can you please add a support for it?
I need to be able to create a record directly from the base class because of the code base that I have.
Obviously I can't refactor everything with out a step by step process. And also I need to support a type field without a class
, like 'other' for instance. I know this issue/proposal was also asked here: #47 and I know the trade offs this could imply. We love this trait but as I've already said, having a large base code we need some way to add functionality in small cycles. I'm not the only one in this situation.
Right now we have a workaround idea. For those how have the same problem.
A Base Class
that extends
from Model
in which we have all of our configuration then the normal class
extends
from it and then the inheritances. So we can call the base
anywhere without problems.
class VehicleBase extends Model
{
protected $table = "vehicles";
// Conventional Model configuration goes here
}
use Nanigans\SingleTableInheritance\SingleTableInheritanceTrait;
class Vehicle extends VehicleBase
{
use SingleTableInheritanceTrait;
protected static $singleTableTypeField = 'type';
protected static $singleTableSubclasses = [Car::class, Truck::class];
}
class Car extends Vehicle
{
protected static $singleTableType = 'car';
}
class Truck extends Vehicle
{
protected static $singleTableType = 'truck';
}
Anyway, This is a great tool. Thanks for the work
Using Laravel 5.3
I'm not sure if I'm expecting something that should be there or not.
class Base extends Model { }
class Sub extends Base { }
class SubAlt extends Base { }
I would expect doing something like this:
Sub::all()
Expectation:
\Collection {
all: [
\Sub { }
]
}
Getting:
\Collection {
all: [
\Sub { }
\SubAlt { }
]
}
Not sure if this is part of the Laravel 5.3 ticket #23 or not.
Is there a way to make a record belong to two types of model at the same time?
Thanks for a great package, we have started to use it a lot.
Recently we have run into a problem when using Single Table Inheritance with Eloquent eager loading.
If I run the below I would get the expected results.
$model = new App\MyModel;
$model = $model->find(1);
dd($model->myRelation);
If I run the below I would get an empty result.
$model = new App\MyModel;
$model = $model->with('myRelation')->find(1);
dd($model->myRelation);
After some digging I found that it worked when there was no Single Table Inheritance.
This lead me to find that SingleTableInheritanceTrait::setFilteredAttributes()
was stripping the pivot table attributes add by the relation.
$attributes
before filtering.
Array
(
[id] => 21
[type] => sub_type
[user_id] => 1
[name] => Test Information
[description] =>
[status] => true
[created_at] => 2016-10-01 10:56:39
[updated_at] => 2016-10-01 10:56:39
[pivot_parent] => 6
[pivot_other_id] => 21
[pivot_extra] => 0
)
$attributes
after filtering.
Array
(
[id] => 21
[type] => sub_type
[user_id] => 1
[name] => Test Information
[description] =>
[status] => true
[created_at] => 2016-10-01 10:56:39
[updated_at] => 2016-10-01 10:56:39
)
All pivot attributes are added with the hard coded prefix pivot_
. The below code works for me.
public function setFilteredAttributes(array $attributes) {
$persistedAttributes = $this->getPersistedAttributes();
if (empty($persistedAttributes)) {
$filteredAttributes = $attributes;
} else {
// The query often include a 'select *' from the table which will return null for columns that are not persisted.
// If any of those columns are non-null then we need to filter them our or throw and exception if configured.
// array_flip is a cute way to do diff/intersection on keys by a non-associative array
$extraAttributes = array_filter(array_diff_key($attributes, array_flip($persistedAttributes)), function($value) {
return !is_null($value);
});
if (!empty($extraAttributes) && $this->getThrowInvalidAttributeExceptions()) {
throw new SingleTableInheritanceInvalidAttributesException("Cannot construct " . get_called_class() . ".", $extraAttributes);
}
$filteredAttributes = array_intersect_key($attributes, array_flip($persistedAttributes));
}
+
+ // All pivot attributes start with 'pivot_'
+ // Add pivot attributes back in
+ $filteredAttributes += $this->getPivotAttributeNames($attributes);
$this->setRawAttributes($filteredAttributes, true);
}
+ protected function getPivotAttributeNames($attributes)
+ {
+ $pivots = [];
+ foreach ($attributes as $key => $value) {
+ if (starts_with($key, 'pivot_')) {
+ array_set($pivots, $key, $value);
+ }
+ }
+ return $pivots;
+ }
Alternatively you could documenting that when eager loading is used and if a relation adds pivot properties using the withPivot() method then all all keys and additional properties need to be added to the $persisted
array property.
My preference would be to not filter out pivot_*
properties.
The hhvm pipeline for the travis builds are failing.
I'm stuck in a scenario where im using Mutators an Accessors in my model. This works fine up until i want to create a model. Because the attributes are set twice. Let me explain.
These are my accessors and mutators. You have to give somekind of enumeration class on those fields. And the mutator tranforms it into a string.
/**
* @param $value
* @return AccessorType
*/
public function getAccessorTypeAttribute($value)
{
return new AccessorType($value);
}
/**
* @param AccessorType $accessorType
*/
public function setAccessorTypeAttribute(AccessorType $accessorType)
{
$this->attributes['accessor_type'] = $accessorType->getValue();
}
This goes fine the first time and the internal attributes table is set with the string.
But after the saved event is catched by the Observer.
The model tries to fill itself a second time but now with the data from the attributes table without using the mutators first.
Problem is internally AccessorType is a string but when used you or set you need it wrapped in the enumeration class. So PHP throws an error because the typehinting fails.
The first setAttribute is called in the
Model->fill() function
The second after
setSingleTableInheritanceType();
and then it calls the magic method __set()
As the title says, it would be very nice to have Laravel 5.7 support as it has been tagged today. From what I've seen in the upgrade guide, not too much has changed, so perhaps it's nothing more than a composer.json
update.
edit: Added a PR: #53
Hello,
I tried to make a pull-request for Laravel 5.5, but I can't figure out how to make the test-cases succeed. Before I made any changes, I ran vendor/bin/phpunit
which gave no errors. Then I made the following changes to composer.json
:
It looks like the migrations are not executed, I've read the documentation on orchestra/testbench, but I don't understand why the migrations are not running.
Hello!
I have tried to sync a belongsToMany relationship and the filter seams to don't apply: all table (even brother rows) is cleared except for those who were sent on form
Have a nice day!
I'm only using the Eloquent ORM (not the whole Laravel framework), and for some reason, I couldn't get your demo work on my project.
Before posting any of my failing code (nothing fancy, just a simple test), I want to know if this package supports the independent Eloquent ORM provided in illuminate/database.
So, is this package meant to be used with the whole Laravel framework or I might have missed something in my demo?
use Nanigans\SingleTableInheritance\SingleTableInheritanceTrait;
class A extends Model
{
use SingleTableInheritanceTrait;
protected $table = "a";
protected static $singleTableTypeField = 'type';
protected static $singleTableSubclasses = [B::class];
}
class B extends A
{
protected static $singleTableType = 'b';
}
// In some other file
$b = new B(); // some inits as well
$b->save(); // This line is throwing an exception
I'm getting this error:
Field 'type' doesn't have a default value
Wrong classes gets instantiated when using numeric type values and calling Parent::all().
This is because getSingleTableTypeMap() uses merge_array to cache parent class types (see. SingleTableInheritanceTrait line 65).
This also caused inverted relation to fail. I'd suggest using:
$typeMap += $subclass::getSingleTableTypeMap());
Hi,
first I want to thank you for this great package, it helps us a lot in several cases within our projects.
After upgrading Laravel to version 5.3 we found a bug. When using pluck() with Eloquent queries where the Eloquent class using STI, the application crashes with following message:
Nanigans\SingleTableInheritance\Exceptions\SingleTableInheritanceException with message 'Cannot construct newFromBuilder without a value for type'
Example:
Lets say we have a class EloquentModel that applies and configures STI, the type field is named 'type'. There is a class MyEloquentModel that inherits the EloquentModel class and configures the type. Now I want to get all IDs from MyEloquentModel, I use following code:
MyEloquentModel::pluck('id');
This leads to the error from above. Do you have an idea how to fix this problem?
Many thanks and best wishes
David
I added an Observer for my User class but it doesn't get triggered when my SuperAdmin (which is a subclass of User) gets created/deleted, etc.
Constructing this object with no attributes and then setting them as "filtered" does not mutate properties that are intended to be mutated.
I have verified that passing the attributes directly to the constructor does mutate the intended properties.
I'm planning on working this until I have a proposed patch, but wanted to give a heads up and see if you had any advice.
Here's my reproduction case in my own codebase: (TP_TradeEstimate and Hours are both subclasses of the same thing)
public function testOnlyTrashedShouldntBreakSingleTableInheritance(): void
{
$liveTP = TP_TradeEstimate::factory()->create();
$deletedTP = TP_TradeEstimate::factory()->create();
$deletedTP->delete();
$liveOther = Hours::factory()->create();
$deletedOther = Hours::factory()->create();
$deletedOther->delete();
self::assertSame(1, TP_TradeEstimate::onlyTrashed()->count());
$affectedRows = TP_TradeEstimate::onlyTrashed()->forceDelete();
self::assertSame(1, $affectedRows, 'Affected rows should match counted rows');
self::assertModelExists($liveTP);
self::assertFalse(
DB::table('account_integrations')
->where('id', $deletedTP->id)
->exists(),
);
self::assertModelExists($liveOther);
self::assertSoftDeleted($deletedOther);
}
This fails because $affectedRows is actually 2, and if you comment out that assertion, the last assertion fails because $deletedOther has been forceDeleted. Even weirder, obviously onlyTrashed by itself isn't the problem, that count works, and if you get()
the collection and do each->forceDelete()
there's no problem.
Using single-table-inheritance v1.0.0
with Laravel framework 9.36.2
Hi folks:
This may or may not be a weird one.
I've got a model (User) which uses Single Table Inheritance to identify six different classes of user. Basically, my parent class looks a bit like this:
namespace MyNamespace\Models;
class User extends Illuminate\Database\Eloquent\Model
{
use Nanigans\SingleTableInheritance\SingleTableInheritanceTrait;
protected static $singleTableTypeField = 'user_role';
protected static $singleTableSubclasses = [
'MyNamespace\Models\UserRole1',
'MyNamespace\Models\UserRole2',
'MyNamespace\Models\UserRole3',
'MyNamespace\Models\UserRole4',
'MyNamespace\Models\UserRole5',
'MyNamespace\Models\UserRole6',
];
}
Then I have this child class (in the MyNamespace\API\Models
namespace this time):
namespace MyNamespace\API\Models;
class User extends MyNamespace\Models\User
{
protected static $singleTableTypeField = 'user_role';
protected static $singleTableSubclasses = [
'MyNamespace\Models\API\UserRole1',
'MyNamespace\Models\API\UserRole2',
'MyNamespace\Models\API\UserRole3',
'MyNamespace\Models\API\UserRole4',
'MyNamespace\Models\API\UserRole5',
'MyNamespace\Models\API\UserRole6',
];
}
The problem I'm finding is that when I do a Model::find(...) on the child user class, I get a model as specified by the parent class's $singleTableSubclasses
property.
Am I doing something stupid? The documentation suggests that this should work?
Things that may be helpful:
Any wisdom on this would be appreciated.
Thanks.
I'm trying to change the column 'type' that stores le class name. I use:
$instance = Model::find($id);
$instance->type = 'other'; // 'other' stands for Other model which shares the same table
$instance->save();
But when saving, type hasn't change. How should we do that with Nanigans/single-table-inheritance ? Actually is it possible to convert a row in the table changing his type column directly ? Thanks!
Hello,
I met a strange behavior applying SingleTableInheritanceTrait on a model used in a BelongsToMany relation:
Single table inheritance structure:
class Animal extends Model {
use SingleTableInheritanceTrait;
}
class Elephant extends Animal { }
Many to Many relation with "single table inherited" model:
class Zoo extends Model {
public function animals(){
return $this->belongsToMany(Animal::class, 'animal_zoo', 'zoo_id', 'animal_id');
}
}
The relation load only when I explicitly "touch" the relation property:
return $zoo->animals;
It doesn't load using "with" or "load" eloquent methods:
// Create relations
Zoo::find(1)->animals()->attach([1, 2, 3]);
// Using "with"
return Zoo::with('animals')->get();
// Using "load" in booted model
$zoo = Zoo::find(1);
return $zoo->load('animals');
/*
* In both case the result is
*/
{
name: 'Super funny Zoo',
address: '...',
animals: [] // <-- empty array for belonsToMany relation using SingleTableInheritanceTrait
}
This example represents a return value for an api call by javascript frontend. It's an easy reproducible issue.
Can I use some tricks to load belongsToMany
relation properly?
Hi,
composer require "nanigans/single-table-inheritance:0.5.*"
Doesn't work:
Your requirements could not be resolved to an installable set of packages.
This is because the lastest version on Packagist is 0.5.0 only "supports" Laravel 5.1.*:
https://packagist.org/packages/nanigans/single-table-inheritance
I see that you updated dev-master
to Laravel 5.2.*:
https://github.com/Nanigans/single-table-inheritance/blob/master/composer.json#L14
Could you publish a new version (0.5.1) on Packagist with new composer.json & Laravel 5.2.* support?
Thanks.
Support for Laravel 5.8 is needed.
Hi,
I noticed there is no record inserted into database..but if I changed the below:
$instance = (new $class)->newInstance([], true);
to
$instance = (new $class)->newInstance();
it works. However, the changes will pose the update problem for duplicate entry in database.
Any help will be much appreciated:) Thanks
Using the following code as an example:
class Zoo extends Model
{
public function animals()
{
return $this->hasMany(Animal::class);
}
}
class Animal
{
use SingleTableInheritanceTrait;
protected $table = 'animals';
protected static $singleTableTypeField = 'type';
protected static $singleTableSubClasses = [ Elephant::class ];
public function zoo()
{
return $this->belongsTo(Zoo::class);
}
}
class Elephant extends Animal
{
protected static $singleTableType = 'elephant';
}
Assuming a Zoo has been saved with some animals associated, calling Zoo::with('animals')->get();
fails with Nanigans\SingleTableInheritance\Exceptions\SingleTableInheritanceException with message 'Cannot construct newFromBuilder for unrecognized type=elephant'
.
This is because get_called_class
at https://github.com/Nanigans/single-table-inheritance/blob/master/src/SingleTableInheritanceTrait.php#L46 returns the base Animal
class which doesn't know about the elephant
type.
A better solution for this would be to define the type in the sub-class map:
protected static $singleTableSubClasses = [ 'elephant' => Elephant::class ];
When I try to update an element that uses STI with the following code :
$questionnaire = Questionnaire::where('chain_id', $chain->id)->first();
$questionnaire->body = 'test';
$questionnaire->save();
the update does not work because the trait drops in an extra binding for the type column (type = 'questionnaire') but the type column is not included into the update query.
There are 4 bindings for 3 question marks :
'query' =>
string(70) "update `form_elements` set `body` = ?, `updated_at` = ? where `id` = ?"
'bindings' =>
array(4) {
[0] =>
string(4) "test"
[1] =>
string(19) "2014-10-03 00:06:13"
[2] =>
string(13) "questionnaire"
[3] =>
string(1) "1"
Eloquent added a retrieved
event in 5.5 that we need to support as well.
I needed to explain STI to some colleagues to find the link that is referenced in the README to "Mark Smith's" blog no-longer exists.
So I had to resort to using Internet Archive Wayback Machine:
Does this work with abstract base types? I've had issues overriding FromBuilder with abstract base types.
I found that if model has subclasses, trait adds whereIn
condition to query.
But the root model (which queries every type) don't need whereIn
condition to select specific types. It will just make query slow.
I think it would be nice to remove whereIn
condition when querying root model.
I have a parent model called Category, and two category types, Link & Dynamic. Only one of them has the relationship criteriable()
When I try to load like,
$categories->load('criteria');
I get the error, Call to undefined relationship [criteria] on model [App\Models\Category\Category]'
Is eager loading that relationship possible?
Laravel 5.3 is close to being released. I'm hoping this package will be updated to work with it.
Hi,
I'm trying to create a structure for different types of users but can't seem to get it to work, i get this error:
Cannot construct newFromBuilder for unrecognized type=agency
class User extends Authenticatable
{
use SingleTableInheritanceTrait;
protected $table = 'users';
protected static $singleTableTypeField = 'type';
protected static $singleTableTypeClasses = [
Agency::class
];
}
class Agency extends User
{
protected static $singleTableType = 'agency';
}
User i'm trying to get with User::find($user_id) has the "type" attribute set to "agency".
I'm using Laravel 5.4
Am I missing something?
Maybe I am crazy, but I couldn't get the overview examples to work on laravel 5.1 until I changed them to the following:
class Vehicle extends Model
{
use SingleTableInheritanceTrait;
protected $table = "vehicles";
protected static $singleTableTypeField = 'type';
protected static $singleTableSubclasses = [Car::class, Truck::class];
}
Note the changing of the $singleTableSubclasses array items from strings to the class name resolution scalars. Using the original 'Car' and 'Truck' strings, I was receiving "Class not found" errors when instantiating those particular classes, but using the class name resolution scalar resolves it for me for some reason. Perhaps it is because I had them in a separate models directory, I am not sure.
Also, it seems that there is no Eloquent class to extend in 5.1, but there is a Model class.
Contract Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\RecurringPaymentMethod;
use App\Order;
use App\ContractAddress;
class Contract extends Model
{
protected static $throwInvalidAttributeExceptions = true;
protected $attributes = [
'validated' => false,
'terminated' => false
];
protected $fillable = ['validated', 'termniated'];
protected $casts = [
'validated' => 'boolean',
'terminated' => 'boolean'
];
public function recurringPaymentMethod()
{
return $this->belongsTo(RecurringPaymentMethod::class, 'recurring_payment_method_id');
}
public function order()
{
return $this->belongsTo(Order::class, 'order_id');
}
public function addresses()
{
return $this->hasMany(ContractAddress::class, 'contract_id', 'id');
}
}
ContractAddress:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Contract;
class ContractAddress extends Address
{
protected static $singleTableType = 'contract_address';
protected static $persisted = [
'start_date',
'end_date',
'default',
'contract_id'
];
public function __construct() {
parent::__construct();
$this->casts = array_merge($this->casts, [
'contract_id' => 'integer',
'default' => 'boolean'
]);
}
public function contract()
{
return $this->belongsTo(Contract::class, 'contract_id');
}
}
Address:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Nanigans\SingleTableInheritance\SingleTableInheritanceTrait;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\ContractAddress;
use App\PersonAddress;
class Address extends Model
{
use SingleTableInheritanceTrait;
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $table = "addresses";
protected static $singleTableTypeField = 'type';
protected static $singleTableSubclasses = [PersonAddress::class, ContractAddress::class];
protected static $persisted = [
'street_line1',
'street_line2',
'zip_code',
'city',
'country',
'validated',
'archived'
];
protected $casts = [
'id' => 'integer',
'validated' => 'boolean',
'archived' => 'boolean'
];
}
This code won't always work:
$contract = new Contract;
$contract->validated = false;
$contract->terminated = false;
$order_id = $faker->numberBetween(1, 5);
$order = Order::find($order_id);
$contract->order()->associate($order);
$recurrent = RecurringPaymentMethod::all()->random(1);
$contract->recurringPaymentMethod()->associate($recurrent);
$addresses = ContractAddress::all()->random(5);
$contract->addresses()->saveMany($addresses);
$contract->save();
The hasMany relationship between Contract and ContractAddress is broken: most of the time I cannot access/save the addresses trought $contract->addresses
.
I also tried to associate a ContractAddress with a Contract like this: $address->contract()->associate(...)
but I get the same weird behavior: contract_id is not always set on the "addresses" table.
Hi there! Great package. Any plans to update for L5? Thanks!
what if i have to retrive all vehicles from vechicle class?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.