Lets say that in my mvc application, I have a model that has many different types. There's one db table but a bunch of subclasses that express slightly varied behavior.
On the page, sometimes I want to show the list of all these model types, and sometimes (when one is currently active) show the details of that one. The details shown are rather small, a few words at most, so I'm having trouble deciding where to put them.
Option 1 (the old pre-refactoring way): In an array with properties for each of the detail fields. The benefit is I can iterate through all the possible types. With classes, there's nothing to iterate through. I'd prefer to get rid of this array.
Option 2: In the model as properties. The views would access these properties based on the currently active type, but I still have nothing to iterate through and show all types. I may have to show them all explicitly (only 9 of them, not terrible.)
Option 3: In separate views, literally written. I would have 9 different views but this lets them be slightly different. The layout of each type could vary.
For some detail, this is for a game and the model is a bunch of actions your character can do. They behave similarly but affect different stats in different ways.
Why not go with something like this?
public interface ICharacter {
IEnumerable<ICharacterAction> Actions {get;set;}
}
public interface ICharacterAction {
string Description {get;set;}
}
Related
I'm pretty new to Laravel, so I'm struggling with the logic for what is essentially a CMS with multiple content types.
Say I have 3 content types; Food, Books and Cars. Every item in all content types has a name, URL and a couple of other fields.
I can create, update and delete any of these resources with most likely the same code replicated 3 times. The only difference would be with a create or update as the field names would differ between them.
Should I just duplicate these fields/functions for each controller, or create some common ground in one place?
The crossover of fields/functions initially will not be huge, however, it seems inefficient let's say if I had 10 content types and I want to add one field to all of them I have to update code in a large number of places.
If I had a central "Node" that contained the id's and common fields for ALL items in every content type, then have this linked to individual tables for the custom fields, I'm in a much better position when I want to add, update or delete common fields.
I've currently got 3 controllers and have only worked on one so far so I have an index(), show() and edit() function in the controller.
As a test, I created a Node model with php artisan make:model Node -mcr and simply extended the existing Controllers so they were extending NodeController. Which just threw up an error like this;
Declaration of App\Http\Controllers\FoodController::show(App\Food$food) should be compatible with App\Http\Controllers\NodeController::show(App\Node $node)
This is likely not the way to go about it anyway, but I simply do not know the recommended practice for this.
Most appropriate and standard best practice for your problem is,
have a single database table, let's say table name as node, which will contain all the common fields, and have another table as categories and relate it with node table (1-m) to categorize type of node such as car,book,food etc., and make one more table, let's say node_meta which will store all additional attributes depending on the type of node,
(you may have a look on the wordpress CMS database ER Diagram which has similar db design.)
Polymorphic relation is not a good idea for this as stated by another user above, it has some limitation when it comes to querying underlying data, for example you cannot apply whereHas query and still there is no official solution to this problem.
I want to make it easy to create drop-down menus within my system that are populated from the database (for example, a list of user groups). I am generally following a domain-driven design approach for this system, including a slightly adapted version of the Repository pattern. (The system is in PHP.)
Since retrieving a drop-down list for a given domain-object class is a common operation, I am wondering if it would be appropriate to create getDropDownList() methods on the relevant repositories.
For example, let's say the domain object in question is called "Category". What I'm proposing is to create a CategoryRepository::getDropDownList() method that would return an associative array of category IDs and titles, ready to be used to create an HTML <select> list.
On a past project, when I created a getDropDownList() method on a repository-like class, one of the other developers said that such a method didn't belong in that class, saying it had more to do with the view than the model. But I don't see it that way because the purpose of the method is simply to return the raw data for the list. It doesn't even need to be used to create a dropdown list; it could be converted to JSON data or any other number of things.
My main questions are:
Does a getDropDownList() method like the one I described belong in a repository class? If not, where should it go instead?
Is this perhaps just a naming issue? Perhaps it would be better if I called it something like getSimpleList() or getArrayForList() to indicate that it's returning an array, and not already-rendered HTML?
To continue the category example, the data returned from this method would return an associative array of category IDs as the keys and category names as the values, e.g.:
array(
1 => 'Category A',
2 => 'Category B',
...
)
IMHO you should seek for a business meaning of every program element. View layer is there just for the sake of presentation of business rules/data and should be easily replaceable. Your repository on the other hand is part of the business model and should definitely follow business naming (names understandable among business people). Thus, your suggested method naming is not valid. "DropDownList", "SimpleList" and "ArrayForList" have no meaning to business heads.
I suggest the following:
by-the-book path (if performance is not an issue) would be method CategoryRepository::findAll()/getAll() which returns all categories in form of Category instances - this way you are dealing with strictly business elements across all layers which is very nice since you don't introduce any intermediate type. In view layer you can easily format this instances into <option/> elements
custom method (as you suggested) but with a name understandable by business ppl - e.g. getTitlesOfAllCategories() (#return string[] Array of category ID => title)
Another problem with getDropDownList() is that it can't be "recycled" easily because of naming issue - imagine the sudden need to list categories inside <ul><li> list - is it time to duplicate your original method with getBulletedList()?:) What about checkboxes - maybe getCheckboxList()? But, the meaning is always the same, you just want to present... ta-daaaam... all categories.
You should do your utmost not to query your domain. Your domain should be focused around full aggregates/entities.
Rather create a separate query layer that focuses on returning data using some agnostic naming.
For instance, in C# I would have something like this:
public interface ICategoryQuery
{
DataTable All();
}
Something like an All method isn't something you would typically find on a CategoryRepository as the domain is concerned with manipulating data (command-side). So if we ever need to perform some action on all our categories so often as to warrant an All method we probably have a design flaw. Come to think, that may indicate that we are querying our domain :)
I am looking for a little advice on database design.
I am constructing a slideshow applicatino on the symfony2 platform, and I have four different types of slideshow containers (company, geo-area, property and individual monitors). This is for the purpose of organizing content and inheriting content down the line (a monitor sits in a property, a property is part of a geoarea etc.).
Now, on flat PHP I would have used only two tables, "containers" and "contents", and put a type field in the containers table defining whether the container in question is a geo-area, property etc. and just linking each content piece (i.e. slide) with a FK to the appropriate container.
Now, learning about symfony2's entity system, it seems that I could gain a lot inheritance-wise by instead defining the different container types as separate entities, thus being able to fetch for instance the geo-area, and automatically get returned all of its child objects (all properties, and in turn all monitors belonging to that property) on the fly. I do however want to be able to switch the "belonging" of a content piece between different containers, and different container types. I sense this will be somewhat hairy with the described approach, seeing there will most likely be a problem with the relation (FK) of the content piece if it must have the capability of "belonging to" any of four different entiy types?
Someone seasoned in the symfony2 world could perhaps enlighten me as to the wisest path to proceed here?
In our project we use Propel 1.6 and PropelBundle
Its syntax is quite developer-friendly.
One great advantage of Propel is "fake" relations between tables. In your schema by just defining skipSql = true flag you omit the generation of FK, but one object can be fetched with another just by defined fake relation.
In your case it will end up with something like below:
$containers = ContainerQuery::create()->findByXXX('xxx');
$containers->getFirst()->getContents(); // Will return all joined content for the first container
During population of objects with related ones - you achieve great opportunity to use reverse relations:
$contents = $container->getContents();
$container = $contents->getContainer();
Just ask in comments, and I'll extend my answer with whatever you need ;)
I have few DB tables, witch are build using inheritance from one table witch is an sort of "template" for creation of new tables, and now i have set of businesses logic methods witch work on columns inherit from template, additional columns are used only as params for presentation of models, they're have no meaning for logic.
The goal is to share businesses logic methods along all of that tables, i know, now it can be done by adding another class witch extends CActiveRecord, and extend from it every model, or pack logic as a behavior and append it to models.
But this will require to write at least "dump" class file for every table/model, but those tables "live" in system, and will be deleted/created with system life cycle.
Is there a way to write some sort of "meta-model" witch will take as a param, table name, or maybe some way to create models for tables "on-the-fly" and append to it businesses logic?
I've asked this question on Yii users board, but did not find any response :/
I'm considering this as some sort of code challenge, so any help / clues are welcome :)
[EDIT]
Some samples:
tables for different client devices
hfc.cable_modem
lan.switch_port
lan.voip_gateway
(in near future, there will be more "technologies" added to system, so new tables for client devices, and there is a possibility to drop support for some of them)
every table inherits from template table client_device witch has that fields:
client_id
service_id
core_device_id
(plus some meta columns for timestampable behavior like created, updated, updater etc.)
like You see business logic operates only on ID's, and its identical for every table, rest of columns are used as device params storage/presentation information.
My goal is to have "meta-model" client device, witch will apply business logic to all this tables, and still provide for every of them, access to specific fields, without having to write model class, for every table (witch i'll have to do, every single time, when new technology will be added, or support for given technology will be dropped in a future)
Well, if I understand you right, I have a suggestion based on something similar I am doing:
I have a base "feature" model. But the feature can be a "text" feature, or an "image" feature, etc. But they all share the common "feature id" and a couple other columns as well. So I took sort of an EAV approach. I have a single "feature" table, and then I have a table for each sub-type (text, image, etc). One of the columns in the "feature" table contains the sub-type info. Then in my "afterFind()" method on the base "feature" model I look at the sub-type column. If the sub-type is "text" I attach a "text" type behavior I made. This behavior gets the variables from the sub-type table and sets them up to be accessed just like attributes of the base model.
Something like this:
client_device_table: (base table)
-client_id (primary key)
-service_id
-core_device_id
-device_type (name of the behavior, like CableModemBehavior, or VoipGatewayBehavior)
cable_modem_table
-core_device_id
-modem_info_1
-modem_into_2
voip_gateway_table
-core_device_id
-gateway_info_1
-gateway_into_2
In the ClientDevice CActiveRecord model (the base model):
protected function afterFind() {
parent::afterFind();
// remember $this->device_type holds the relevant behavior i.e. CableModemBehavior
$this->attachBehavior($this->device_type,call_user_func(array($this->device_type, 'model')));
}
And the behavior looks something like this:
class CableModemBehavior extends CActiveRecordBehavior {
public modem_info_1;
public modem_info_2;
public function attach($owner)
{
parent::attach($owner);
$connection = Yii::app()->getDb();
$command=$connection->createCommand("SELECT *
FROM cable_modem_table
WHERE core_device_id=:device_id");
$command->bindParam(':device_id',$this->owner->core_device_id);
$data=$command->queryRow();
$this->modem_info_1 = $data->modem_info_1;
$this->modem_info_2 = $data->modem_info_2;
}
}
This is untested, but what SHOULD happen now is if you get a ClientDevice model with CableModemBehavior as it's sub-type column entry, you will be able to access the modem attributes (modem_info_1) just like the regular ClientDevice attributes (client_id):
ClientDevice->modem_info_1
There is going to be more to it than this, of course. This is just for the "find" case. You will need to do some more work to get the mass attribute assignment thing to work for $_POSTs, or to transfer Relations, or to add afterDelete, validate and afterSave methods to support saving and deleting, etc, but I hope this is a helpful start.
You could also make this a lot nicer by overriding the __get and __set methods of the base model in the behaviors so that if a column from the sub-type table is requested, it goes and get's it from the text table transparently, doing a schema lookup to get the column names, etc. Better than hard coding it like I did in this example. It would be helpful to look at the EavBehavior in the yiiext repository and the AdvancedArBehavior (or similar ones) to get a handle on how to make it slicker. Instead of a behavior for each sub-type, you could have a generic behavior and just pass in the sub-types table name. (ooo I like that actually)
Cheers!
Currently, I am working on restructuring an existing code base. I'm new to php frameworks, but I do know in general how MVC works.
Right now, there is one controller file, one model file, and thirty view files.
Should every model correspond to a table?
Should every view correspond to an html page?
What about the controller? How can I break this thousand line monster into more organized code.
Thanks.
Should every model correspond to a table?
No. A model is often constructed from data from multiple sources. Don't think in terms of tying it to your physical database structure even though there will probably end up being lots of similarity.
Should every view correspond to an html page?
Not to sound trite, but every view should correspond to a view. I'm not sure exactly what you mean by a "page".
Perhaps an example would be useful. Imagine a user registration page. The model is User and might contain fields such as:
Title
Given names
Surname
Date of Birth
Username
Address(es)
Email address
Phone number(s)
etc
Now, that data may be in multiple tables. For example: Party, Person, Contact and Address.
There will probably be several views:
About page
Form page (used for new registration and possibly editing details as well as errors);
Success page;
Failure page.
Typically all of this will be handled by a single controller as all the processes are inter-related.
Every model should correspond to a logical data object - which should generally predominantly be stored in one table (often with foreign keys into other tables, since models generally need to reference other models).
Every view should correspond to a logical way of viewing your data (e.g. on stackoverflow, there is hopefully a view for the list of badges pages, a view for the list of tags pages etc).
Every controller should correspond to a logical grouping of views, which should not be too big (where too big is the line where the file is becoming unmanageable - if you've got 30 views, you can hopefully find a logical way to group them into say 3 controllers).
What about the controller? How can I
break this thousand line monster into
more organized code.
Have a look at CakePHP framework and how it solves the problem of large models, controllers, and numerous views. I find it quite elegant. Complex models can have behaviours. Large controllers can be broken into components. And numerous views are grouped with layouts, while having common bits separated into elements. It might sound complicated and scary at first, but once you try to use it, it really falls into place.
Should every model correspond to a
table?
It doesn't have to but it often will depending on the complexity of your business logic.
Since you're refactoring an existing application, think about how the model is used by the other layers. In MVC, the model is at the bottom of the dependency stack.
How will the view access the model? How will the controller modify it? How will the model be populated?
Should every view correspond to an
html page?
Again, it doesn't have to but it often will.
What about the controller? How can I
break this thousand line monster into
more organized code.
A common strategy is using the front controller pattern. The front controller deals with HTTP requests, application initialisation and site-wide logic (just as your thousand line monster is currently doing) - but then it delegates to more specialised controllers.
These specialised controllers could be grouped by the models it uses, site page structure, or anything else that seems logical. They then interact with the model and select a view to display.
Finally, +1 to frameworks as Leonid suggested. Even if you don't end up using one, there are some great implementations of controller patterns out there.
Hope that helps.