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!
Related
I have an application that has only two tables: products and type - where each product can have multiple types as attributes. I decided to normalize the database and created another table where I intend to keep the relations between the products and types.
(Disclaimer: I'm relatively new to object oriented programming). I have a class for products, and a class for types. I have been told that I should have a class for every table that I have in the database. Does this also apply to a table created for normalization purposes? If so - what is the best way of dealing with this - should I somehow call both other classes in this third class, or keep it an independent class, and just manage the exchange of information through actual forms on the webpage, etc?
If your product_types table only holds foreign keys there is no need to map it to its own class. Since this is defining a many to many relationship between the tables you can just provide a method in your product class to get the types.
getTypes() {
// retrieve the types for this product and return
}
Then add the opposite method in the types object to get the products.
getProducts() {
// retrieve the products for this type and return
}
I have been told that I should have a class for every table that I have in the database. Does this also apply to a table created for normalization purposes?
Usually when you're designing a database, the first thing you need to do is create a conceptual data model. This will allow you do define your entities as well as defining relationships between them. Then you create a logical data model to characterize and refine your entities. The last step is the physical data model, which is the closest model there is to a database. Now in this model your entites are now tables, and some of them may or may not relate to your application domain.
For example you could have a travel agency application, where you'd have tables for destinations, flight companies etc... These would map directly to your application because they represent concrete classes. On the other hand you'd also have configurations, sparse data (billing...) or associative tables (like you have here). They don't map to your current application. This concept is called impedance mismatch. See this diagram I found online :
Finally to answer your question : no you don't need to map it to a class, because it has nothing to do in the application domain. Of course you still need to handle it some way (using DAO and SQL basically). You could also use an ORM, like suggested in the comments by #RobW, which can abstract and map directly your database.
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 have few lookup tables which are listed below. As of my understanding we need a Model for each database table, but does this also apply to lookup / mapping tables? what is the best practice used while creating models? Below is a sample of my lookup tables...
Transaction Customer Transaction_Lookup
id Id transection_id
date name customer_id
active active
I have created models corresponding to Transaction and Customer tables, do I need to create a corresponding model for Transaction_Lookup as well?
Also I am using Data Mapper pattern which means I will also have to create a Mapper class for each model...
Thanks for your help in advance....
One remark I would make is to not think of models as corresponding one-to-one with tables. That can be very limiting in your OOP design. That said it is true often your models will align to a single table. Look-up tables being an obvious scenario where this is true.
As for needing a concrete model for each lookup table... I would consider writing a generic Model that could be used for all objects corresponding to 'lookup' tables. You could then use that generic class, or write it as an abstract and extend it and create named concrete classes. The amount of unique code needed there could be very limited, leveraging what you already have on the parent class. If you wanted a look up table specific abstract you could abstract the fields key(id), value, friendly name, active? and map the corresponding fields on the tables to those generic properties. Really a number of ways to go about that, hope I explained it well.
I typically write two styles of mappers, ones that are essentially table row gateways like Zend_Db_Table and another that is more custom, where perhaps I use a stored procedure or complex Zend_Db_selects joining off multiple tables. Using the table row gateway style mappers I usually just need to specify a table name, adapter, and mapped object for the mapper to work with. On the custom mappers I usually need to write more of the implementation code from scratch on a case by case.
I like this approach of using a Data Mapper. Can be both convenient and powerful.
Some background:
This is for an MVC application built without using any frameworks, but follows the MVC paradigm.
I am using an ORM library called RedBean to talk to the database.
While RedBean represents data using "beans", it supports wrapping them into a model, so custom functions may be provided by the model:
$account = R::dispense('account');
$account->name = "john";
R::store($account);
$account->getFormattedLastUpdatedTime('America/New_York');
Now the question:
Currently, each instance of the model would represent 1 row in the database. If I have a collection of accounts, then I would have an array of "account" models.
In the application, I have a feature for custom profile fields (don't worry, I am not using EAV though :)). One of the tables stores metadata for those fields (name, description etc) for generating the form fields for those custom profile fields. Once again, each row of the metadata represents 1 form field and each row is represented by 1 model.
I now wish to write a method to convert all those rows into a form object which can then be used for rendering and processing the form. But, where should this method live? My initial thought was to place it in the model representing the custom profile field metadata.
Clarification: This function would not be in the account model, but instead in the profile_fields_meta model.
Problem
As each model should represent 1 row, it seems a bit "dirty" to have the model return an object that would be generated from MULTIPLE rows in the database. Am I correct to say this is not the best way to do it? What do you recommend I do instead?
It would be right to have extended ArrayObject (http://php.net/manual/en/class.arrayobject.php) or other container class to run methods for collection.
Try to modify query methods to return data in your custom collection class instead of array if specified.
There's nothing inherent to MVC that says "each model [instance] should represent one row." Often in MVC frameworks the model (as seen by the controller, at least) is entirely ignorant of the data store and doesn't have any concept of or direct mapping to a "row." This isn't necessarily the case with ORMs but a model needn't adhere to an ORM's constraints.
However, though it's hard to tell without knowing more about your schema and implementation, the functionality you're describing doesn't sound appropriate for your Account model. In fact, it sounds to me like you should consider having a "FormField" model such that, in Rails parlance, Account "has many" FormFields.
And for the record, EAV isn't always bad, it's just often misused.
Ok, I couldn't insert the overview image, but I'm required to create a PHP app for my studies, it must allow student to register, and the administrator to edit course and student info.
We are not required to code it in an OOP style, but since its the best programming practice to code in OOP, why not learn OOP from the beginning.
I'm a beginner but I know the basics about OOP, classes, inheritance, setters and getters and all that cool lingo, but I'm struggling to decide which parts of this app should be objects, should I make course and student classes or add, edit and delete classes? Any advice on how to approach and visualize such a problem would be appreciated.
Very roughly: This is how I would do it:
Store your data in SQL or XML. You will need two SQL tables, one for Students and one for Courses. You can use one XML file containing all the data, or you can use two files (which I recommend).
Create a class called, for example, dataItem with a property like '$arr_fields' corresponding to a single data record (a single row in a SQL table, or an XML record).
The dataItem class should have the following methods (all public):
loadFromSQL() (or loadFromXML())
saveToSQL() (or saveToXML())
add(), edit() and delete()
a view() method using HTML
These methods are obviously used to read and write data between the SQL/XML data and $arr_fields of the class, and to display the data in $arr_fields. The keys of $arr_fields are the SQL column names (or XML tag or attribute names) for the specific SQL table.
Try not to call loadFromSQL() or saveToSQL() in your constructor or in any of the other methods which are used to modify only the class data. Keep these actions separate. EDIT: This is a personal preference which helps me to keep track of the state of my objects.
Create Student and Course classes that extends the dataItem class.
You can override methods, for instance the view() method, inside your extended classes if you need to.
Then you can call the methods in Students and Courses from an Admin object (like rcdmk suggested) or maybe from StudentFolder and CourseFolder classes whose view() method contains buttons for the actions that need to be performed. (Let StudentFolder and CourseFolder extend a Folder class that you create).
UPDATE:
For example: If your primary key in a SQL table is id, then dataItem's loadFromSQL($id, $tablename) should set $arr_fields so that its keys are the column names and it's values are the values from the row whose primary value is equal to $id.
In Students, you can then override loadFromSQL() as follows:
class Students extends dataItem {
// other attributes
public function loadFromSQL($id) {
parent::loadFromSQL($id, "Students");
}
}
EDIT: On reconsideration, it might be better to set $arr_fields["id"] = $id and also set $tablename with the constructor for dataItem - then you never have to override loadFromSQL() or specify parameters for it. loadFromSQL() should then load the record if it exists. saveToSQL() should save $arr_fields in SQL if $arr_fields["id"] is set and create a new record if it is not set. Anyway, you must find a consistent way of interacting with the data which works for you, these are just possibilities.
However, if you are not experienced with OOP and SQL or XML, you might be opening a can of worms for yourself and it might be better to just do your assignment using functions only and php arrays for your data. Unless you have some time to learn...
From a simple perspective:
Abstract the main objects as classes and use methods for actions of this objects:
Student (object) are deleted (action) by the Admin (object), so
Admin class will have a deleteStudent method, because Admin deletes Students.
Another aprouch is to concentrate all Student related actions in the Student class:
Student class will have a public delete method that Admin can use.
Anyone that think this in better ways of explanation can edit this wiki.
Think about which aspects of your system are actually objects, you know, something you can do something with. The methods are what you do to the objects. So, you're on the right track with course and student classes, and add, edit, and delete would be methods of those classes.
But don't get too bogged down with it. If it's not your core assignment objective, you could quickly get in over your head by trying to do everything exactly the right way. If you can formulate a clear way to get to where you need to go, then go for it, if it seems confusing, back off it a little and learn some more.
You say that you know the basis of OOP, however you ask whether you should create Course, Student classes OR Add, Delete, Edit classes. Well maybe there are other practices, but I guess the most popular one and the only I am aware of is to use nouns as classes and verbs as their methods. Hence, intuitively there is something wrong with class "Add" or "Edit". What I would have done if I were were, is to think of all "entities" that might be considered an object - like Student, Course, Lecturer, Class (Room) and depending on how advanced your model should be you can add more like Building etc. Then try to implement basic things like creating new student, registering for a course, associating teacher with a course etc. Once you have it in place and IT IS WORKING you might want to add advanced things, like inheritance. For example you might want to say, that both Teacher and Student are a Person so you might want to create such abstract class and use inheritance.