How to store polymorphic related models - eloquent

I am working on a system that have lots of users each has his own information so I needed to create a model for each. On the other hand, all those users have a common user model where their credentials being collected form those requirements it was suitable to have a polymorphic relation between all user types and the user model i.e., coordinator as a model and the user as a model I did the following
class Coordinator extends Model
{
protected $fillable= ['userid', ...];
...
public function user()
{
return $this->morphOne(User::class, 'userable');
}
}
class User extends Model
{
...
public function userable()
{
return $this->morphTo();
}
}
class CreateUsersTable extends Migration
{
public function up()
{
$table->bigIncrements('id');
...
$table->morphs('userable');
}
}
class CreateCoordinatorsTable extends Migration
{
public function up()
{
$table->bigIncrements('coordid');
...
$table->foreign('userid')->references('ID')->on('wp_users')->onDelete('cascade');
}
}
After migration I noticed that columns userable_type and userable_id not allowing null. How come I create a coordinator entity with its associated user entity?

I got it after googling...
Laravel One to Many Polymorphic Relationship - Create Records
The idea is not as I thought at the beginning. When I used the $table->morphs('userable'); the user table had two columns, userable_id and userable_type, and each allows no null by default and by ORM convention and I was thinking that they consider the user table is the master and other tables (i.e., coordinator, educator, shipper etc.) each as the detail table. According to this initial wrong understanding I was adding a userid column at each table to store the related user id for each specific user type and I was wondering how I am going to save the user that needs the userable_id and userable_type to be filled by saving the specific user first to get its id that will be provided to the user's userable_id the case resembles a deadlock situation as each table needs a piece of information that will be known after storing data on each to be able to save data to each table, wired!!!.
However, when I read the article in the above link I figured that it considers the table in an opposite way, unlike my thinking was (i.e., coordinator, shipper, etc. are the masters and the user table is the detail). That is for guys who use the models directly but for those who use repository package it is a little bit tricky and you need to do an extra work...

Related

In Objection.js, what's the benefit of setting up relationMappings?

I'm kind of confused about what relationMappings do inside a Objection.js Model class.
I thought once we setup the relationMapping inside a Model, we will get related data in every query. But, it turns out that I still only the Model properties itself.
Is there anything else I should use to get related data in query?
Relation mappings gives model semantics how relations can be fetched when they are needed. It would be really bad for performance to always query all related rows in addition to main table's row. When you create relation mappings to model, you will not need to write joins manually every time you need to query relations. Also they enable many other objection features, which requires information how row relations goes in DB.
To use relation mappings in query Objection.js requires that within every query you must tell which relations you want to fetch with the main row with .withGraphFetched or .withGraphJoined methods https://vincit.github.io/objection.js/guide/query-examples.html#eager-loading
for example:
class Person extends Model {
static get tableName() {
return 'persons';
}
static get relationMappings() {
return {
pets: {
relation: Model.HasManyRelation,
modelClass: Animal,
join: {
from: 'persons.id',
to: 'animals.ownerId'
}
}
};
}
}
const people = await Person.query().withGraphFetched('pets');
// Each person has the `pets` property populated with Animal objects related
// through the `pets` relation.
console.log(people[0].pets[0].name);
console.log(people[0].pets[0] instanceof Animal); // --> true
Mappings are also used when you insert nested object data with .insertGraph so that related objects are inserted to related tables and foreign key references etc. are automatically filled according to relation mapping declarations.
There are many other places where those are used, but I hope this gives a rough idea why they exist.

How to use model with multiple identical tables, for data isolation?

I am writing a simple SaaS application for small construction companies to use and am trying to achieve mid-level data isolation by having each company have their own unique data tables that aren't shared.
This is also nice in case the WHERE {group_id} clause is missing, other group data won't be exposed.
I am able to use the command builder to create these tables dynamically, prefixing them with the group number like grp_37645_projects.
But I am stuck on how to use my model classes as the table names change.
After login, I want to set the table names. These won't change as users aren't allowed to be a part of more than one group.
I have read about changing the tableName, but that is a STATIC function, and I have read a little about creating classes on the fly, but neither option was detailed or complete.
I also know this touches on the single table inheritance, but once again, every example use a little different scenario.
Do you have a recommended solution for setting the tableNames dynamically?
Add some logic for tableName:
namespace app\models;
use yii\db\ActiveRecord;
class Project extends ActiveRecord
{
/**
* #return string the name of the table associated with this ActiveRecord class.
*/
public static function tableName()
{
//some logic for getting current "group_id" for current user
$current_group_id = \Yii::$app->user->identity->group_id;
return 'grp_'.$current_group_id.'_projects';
}
}

JPA: User-Entity contains another User-Entity

In my domain model i have a user entity, which contains another user (composite pattern). When i want to persist the user, i get an error which says something like "Data too long for column...". This happens because the contained user is tried to be stored as a BLOB. I want the ID of the contained user to be stored - not the whole object as a BLOB.
What kind of annotation must i user in the case? The cardinality is 1 : 0..1 - so a user can contain another user.
here is the code ...
#Entity
#Table(name="flex_app_user")
public class User implements Serializable {
private User client;
public User(){
}
#OneToOne
public User getClient() {
return client;
}
}
... and the problem resulted by a change in the datamodel. The correct annotation is OneToOne. And everything works fine.
kind regards,
Jochen
The annotation must be #OneToOne.
As you are using the composite pattern can you use (fetch=FetchType.LAZY) on the relation ship used?

Delete a child from an aggregate root

I have a common Repository with Add, Update, Delete.
We'll name it CustomerRepository.
I have a entity (POCO) named Customer, which is an aggregate root, with Addresses.
public class Customer
{
public Address Addresses { get; set; }
}
I am in a detached entity framework 5 scenario.
Now, let's say that after getting the customer, I choose to delete a client address.
I submit the Customer aggregate root to the repository, by the Update method.
How can I save the modifications made on the addresses ?
If the address id is 0, I can suppose that the address is new.
For the rest of the address, I can chose to attach all the addresses, and mark it as updated no matter what.
For deleted addresses I can see no workaround...
We could say this solution is incomplete and inefficient.
So how the updates of aggregate root childs should be done ?
Do I have to complete the CustomerRepository with methods like AddAddress, UpdateAddress, DeleteAddress ?
It seems like it would kind of break the pattern though...
Do I put a Persistence state on each POCO:
public enum PersistanceState
{
Unchanged,
New,
Updated,
Deleted
}
And then have only one method in my CustomerRepository, Save ?
In this case it seems that I am reinventing the Entity "Non-POCO" objects, and adding data access related attribute to a business object...
First, you should keep your repository with Add, Update, and Delete methods, although I personally prefer Add, indexer set, and Remove so that the repository looks like an in memory collection to the application code.
Secondly, the repository should be responsible for tracking persistence states. I don't even clutter up my domain objects with
object ID { get; }
like some people do. Instead, my repositories look like this:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
The AggregateRootDataModel class is what I use to track the IDs of my in-memory objects as well as track any persistence information. In your case, I would put a property of
List<AddressDataModel> Addresses { get; }
on my CustomerDataModel class which would also hold the Customer domain object as well as the database ID for the customer. Then, when a customer is updated, I would have code like:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
{
public Customer this[int index]
{
set
{
//Lookup the data model
AggregateRootDataModel model = (from AggregateRootDataModel dm in this
where dm.Customer == value
select dm).SingleOrDefault();
//Inside the setter for this property, run your comparison
//and mark addresses as needing to be added, updated, or deleted.
model.Customer = value;
SaveModel(model); //Run your EF code to save the model back to the database.
}
}
}
The main caveat with this approach is that your Domain Model must be a reference type and you shouldn't be overriding GetHashCode(). The main reason for this is that when you perform the lookup for the matching data model, the hash code can't be dependent upon the values of any changeable properties because it needs to remain the same even if the application code has modified the values of properties on the instance of the domain model. Using this approach, the application code becomes:
IAggregateRootRepository rep = new ConcreteRepository([arguments that load the repository from the db]);
Customer customer = rep[0]; //or however you choose to select your Customer.
customer.Addresses = newAddresses; //change the addresses
rep[0] = customer;
The easy way is using Self Tracking entities What is the purpose of self tracking entities? (I don't like it, because tracking is different responsability).
The hard way, you take the original collection and you compare :-/
Update relationships when saving changes of EF4 POCO objects
Other way may be, event tracking ?

How to model a n to m relation with an attribute in Entity framework without adding the extra table

I'm pretty new to the Entity framework and I'm modelling this simple structure:
With this model what I have is a Users class with a property UsersGroups (a collection of UserGroups objects).
I would like to have a Users class with a property like Groups with type Tuple or something like this (a new PriorizedGroup class, etc) that is much more related with the bussines.
Is this possible with the Entity framework?
Thanks in advance.
EDIT: If I were modeling the bussines objects I would create a User class with a Groups property that contained all the groups the user pertains with an extra property to store its priority (with a tuple, with an inherited class, as you wish). The thing is that I feel that the objects created by the Entity framework resemble the SQL structure, not the business structure.
Not directly. EF can map the relation only in the way you see it at the moment but you can add your custom behavior to your partial part of the entity. The simple way is something like
public partial class Users
{
public IEnumerable<PrioritizedGroup> Groups
{
get
{
return UserGroups.Select(ug => new PrioritizedGroup
{
Priority = ug.Priority,
Id = ug.Group.Id,
Name = ug.Group.Name,
Description = ug.Group.Description
})
.OrderBy(g => g.Priority);
}
}
}
To make this happen directly in EF you need some advanced mapping technique which will require you to modify EDMX source code directly (either DefiningQuery or QueryView) and it will make the entity read only (you will need stored procedures for modification).
To make the collection exposed on Users updatable you would probably need to use ObservableCollection and transfer all modifications triggered by ObservableCollection back to original UserGroups collection. Once you have something like that implemented you can hide original collection.