Jump to content

Best way to organise and inherit models with relations?


Sarke

Recommended Posts

This is a post I made on the Yii forum, but it pertains to PHP in general as well.  Any advice or insight would be appreciated.

 

---

 

So, I have different aspects of the app: front-end ui module, output generation (pdf reports, etc), console commands and workers.

Using the standard examples, let's say we have Book and Author models. These would all reside in /app/models/.

Now, the issue is that there is tons of code that need to go into these models that relate only to the ui. There's tons that relate only to the output generation, etc.

So to organise this I see a few options:


1. Put all the code in the common models.

This is a bad idea IMO because it just creates a bunch of noise for when you are working on a specific aspect of the app. Much of the code does not relate to what you are doing.


2. Extend the models in each module.

This seems like the obvious one. Have all the common relations and code in the common models, and then have child classes (app/modules/ui/models/Book extends app/models/Book) contain the specific code relating to to that module.

The issue with this is that the relations will return the wrong class: app/modules/ui/models/Book would have a relation author that returns app/models/Author instead of app/modules/ui/models/Author

Or, I have to re-implement all the relations in the child classes, and that's really not DRY.


3. Use traits

This one seems wrong to me, but it does allow me to organise the code a bit. Basically, instead of using child classes that cause the aforementioned issues with relations, I would organise the "child class" code into traits instead, which would be used by the common models.


So far I am leaning towards #3, but I really feel #2 is the correct solution but I don't know how to do that without repeating all the relations (and this app has a lot).


One of the issues I have with Yii2 is the relation management. Each model will need it's own Query class for the IDE to properly be able to understand that it's a Book being returned by the Query, not an ActiveRecord. So far I've automated this, but having to add yet more Query classes would seem bloated.


Thoughts? Advice? 

Link to comment
Share on other sites

You make it sound like you can only use one OOP feature or technique.  

 

 


Now, the issue is that there is tons of code that need to go into these models that relate only to the ui. There's tons that relate only to the output generation, etc.

 

 

The obvious answer is to separate "views" of the data or data transformation from the data models themselves. WIth an MVC based framework this would be assumed.    I've never done much with Yii, but at least in Yii 2, there is already support for views:  http://www.yiiframework.com/doc-2.0/guide-structure-views.html

Link to comment
Share on other sites

You make it sound like you can only use one OOP feature or technique.

If you have a suggestion, let's hear it. If I extend the models I don't need to use the traits to store model-specific code. The latter is more of a workaround. That doesn't mean I am not using traits in other places.

 

 

The obvious answer is to separate "views" of the data or data transformation from the data models themselves. WIth an MVC based framework this would be assumed.    I've never done much with Yii, but at least in Yii 2, there is already support for views: http://www.yiiframework.com/doc-2.0/guide-structure-views.html

I am using Smarty views for all my HTML output. Or are you talking about view classes for each model, in which case would you elaborate? I don't see how model getters and such would be appropriate in a view.
Link to comment
Share on other sites

This is a post I made on the Yii forum, but it pertains to PHP in general as well.  Any advice or insight would be appreciated.

If you cross-post, you should include a link to the other threads so everyone can track the conversation. Not a big deal here since your other thread appears dead, but something to keep in mind for the future.

 


 

I'm not familar with Yii and it's design/models but if it's anything like Symfony and it's entities then this statement seems incorrect:

 

Now, the issue is that there is tons of code that need to go into these models that relate only to the ui. There's tons that relate only to the output generation, etc.

Your models should be fairly lean, with only code used for managing the model's data. Other code that does stuff with the models belongs else where, such as controllers or services.

 

For example, if you had code that generates a table of contents for a book, rather than putting that code into your book module as

$toc = $book->generateTableOfContents();
You'd have a service that contains that code and you pass it a book as a parameter:

$tocGenerator = new TOCGenerator();
$toc = $tocGenerator->generate($book);

Each model will need it's own Query class for the IDE to properly be able to understand that it's a Book being returned by the Query, not an ActiveRecord.

Again, not a Yii guy so maybe there's more to this statement, but on the surface it sounds like something that could be easier solved with some phpdoc comments rather than auto-generating a bunch of classes just for the sake of the IDE.

/** @var Book $book */
$book = $query->find(123);
The comment tells the IDE/reader that $book is an instance of the Book class. What type the find method is documented (or inferred) to return is overriden.

 

If there's a reason the generate classes beyond just helping the IDE with type inference then great, but I wouldn't go through the hassle just to avoid a few clarifying phpdoc comments.

Link to comment
Share on other sites

If you cross-post, you should include a link to the other threads so everyone can track the conversation. Not a big deal here since your other thread appears dead, but something to keep in mind for the future.

 

Sorry about that, I will do in the future.

 

 

I'm not familar with Yii and it's design/models but if it's anything like Symfony and it's entities then this statement seems incorrect:

 

 

Your models should be fairly lean, with only code used for managing the model's data. Other code that does stuff with the models belongs else where, such as controllers or services.

 

For example, if you had code that generates a table of contents for a book, rather than putting that code into your book module as

$toc = $book->generateTableOfContents();
You'd have a service that contains that code and you pass it a book as a parameter:

$tocGenerator = new TOCGenerator();
$toc = $tocGenerator->generate($book);

 

Yeah, I've used some code analyzers and they don't like the size of my models either.  I think Yii is more in the "fat models, thin controllers" camp, but it also has Behaviors and Events that attach to them.

 

I will refactor it to use services (or something else), thank you for your advice.

 

 

Again, not a Yii guy so maybe there's more to this statement, but on the surface it sounds like something that could be easier solved with some phpdoc comments rather than auto-generating a bunch of classes just for the sake of the IDE.

/** @var Book $book */
$book = $query->find(123);
The comment tells the IDE/reader that $book is an instance of the Book class. What type the find method is documented (or inferred) to return is overriden.

 

If there's a reason the generate classes beyond just helping the IDE with type inference then great, but I wouldn't go through the hassle just to avoid a few clarifying phpdoc comments.

 

 

I do realize I can use the inline phpdoc blocks every time I use it, but I would much rather set this up once for each model so the IDE knows what the class really is.

 

The issue is how Yii2 handles relations. In Yii1 the relations would always just return a the model or model[], so that was easily specified in the model's phpdoc with something like:

 

/**
 * @property-read $author Author
 */

However, in Yii2 you can use either the relation results directly ($book->author) or get the query with $book->getAuthor(). The problem is this query, because it will be a generic ActiveQuery class, and it's one() will only specify that it returns an ActiveRecord, not an Author class.

 

So, to accomplish this I create an accompanying AuthorQuery class using Yii's code generation module. Now the IDE knows that $book->getAuthor()->one() returns an Author model instead of a generic ActiveRecord.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.