I have been using Doctrine ORM for a while an now, I have a class-level property (static property) which I need to persist in MySQL database and I would like to now how.
Class Student {
private $name;
public static $instances = array();
public __construct($name) {
$this->name = $name;
self::$instances[] = $this->name;
}
}
According to the Documentation (Basic Mapping > Property Mapping):
The next step after marking a PHP class as an entity is mapping its properties to columns in a table.
To configure a property use the #Column docblock annotation. The type attribute specifies the Doctrine Mapping Type to use for the field. If the type is not specified, string is used as the default.
It sounds like doctrine only supports object-level properties. But as the title reads "Basic Mapping", I think there should be some type of "Advanced Mapping" that maybe covers static properties. I searched for that with no success.
Also it is not listed at Limitations and Known Issues
Question
Someone please let me know if this is possible to persist static properties in Doctrine 2, and if not, How should I accomplish this task? Any work arounds or something?
Unfortunately, my reputation does not allow commenting, so I have to write this as an answer. Sorry for that.
My first thought is that you are going about this the wrong way. Can you give some more information what the "instances" actually are? It looks like it's a list of all student names in the system? Why do you need to have this as a static class property?
Some thoughts:
1)
It sounds like doctrine only supports object-level properties.
Yeah, I think that's correct, but I don't see the use case for anything else really. To me it feels intuitively wrong to have Static data in a database as it's not meant to change (very) often. Can't you just have your values in the code or in a config file (if there are too many). If, on the other hand, your data is changing often, then it's not Static (<=> not changing).
2) If you really want to map it and have it in the db I believe association mappings is the way to go (one-to-many or many-to-many). I.e., you should move it to its own Entity that has a relation to the Student entities. In your case it looks like you should create a University entity that has a list of all students. And that way you can iterate to build a list of all names.
Related
According to doctrine documentation am reading, it says concerning using
Entity classes
that all of the fields should be protected or private (not public) and this is the quote.
When creating entity classes, all of the fields should be protected or
private (not public ), with getter and setter methods for each one
(except $id ). The use of mutators allows Doctrine to hook into calls
which manipulate the entities in ways that it could not if you just
directly set the values with entity#field = foo;
While the 6th edition of an advanced PHP book(One of the best selling books on PHP and other programming books out there are being written by this company) I just read says this
In most cases, private properties are strongly preferred over public
ones. However, in the case of entity classes, you should use public
properties. The sole purpose of an entity class is to make some data
available. It’s no good having a class representing an author if you
can’t even read the author’s name!
I understand that the pattern used by doctrine might slightly be different from the book approach but when you see statements like this, you get to wonder which is which. Which of the statement is wrong and which of the statement is right
The entire house should please enlighten me
I'm facing some object relationship problems in an application that, among other things, handles multiple choices exercises.
Thinking of those exercises, I can that:
1- I have several exercises, that have several questions that have several alternatives.
2- A specific question can be part of several exercises.
3- A specific alternative can NOT be part of several questions.
In terms of relationship:
Exercise < n-n > Question
Question < 1-n > Alternative
I coded the classes the way bellow:
class Exercise {
private $id;
private $name;
private $questions = []; //array of ExerciseQuestion
//...
}
class ExerciseQuestion {
private $id;
private $exercise = new Exercise();
private $question = new Question();
private $order; //handles question's order of appearance
//...
}
class Question {
private $id;
private $wording;
//...
}
class Alternative {
private $id;
private $question = new Question();
private $text;
//...
}
I don't know if I did it wright, but, instead of storing relationships ID's in classes attributes, I'm storing an instance of the related class. It seemed to be right approach when I started.
The problem I foresee is that, in the relation Exercise-ExerciseQuestion-Question, I will be thrown in a hell of circular references. I will have an Exercise that has several ExerciseQuestion that holds an instance of Exercise with a lot of ExerciseQuestion inside it. Am I right in this assumption?
Is there a better - or correct - way to express that kind of relationship? Should I change to store ID's instead of instances or can can make use of some design pattern?
If I'm reading you correctly, you don't actually have a problem. If your domain includes circular or self-referencing relationships, then your models should too.
Since your objects are entities (they have identities), you'll want to take care that only one instance exists for each id. But as long as you enforce that, they can refer to each other however you need them to.
Now, circular references can be tricky to deal with if you need to traverse the object graph in some exhaustive way. In that case, you'll need to take care to not get yourself into an endless loop. But if your domain actually has these relationships, then you need to model them, and you'll have to implement some way to detect cycles and deal with them. The details will be informed by your particular needs.
I am not sure if i understand what you're saying but if you're asking : is that a circular reference problematic? the answer is yes an no, it's depends of what's you're going to do and how you're handling your issue.
But if you are talking about your implementation the answer is definitly no it's not problematic because the class attribute does not store "an objet" but "a reference to an object" hence there is no circular construction wich lead to an infinite recursive iteration in your case
Good luck
You have a problem in your model. A relation object should store only the ids of the parts involved in the relation, an exercise_id and a question_id, and any additional attributes that apply to the relation of both entities, such as the order. You don't instantiate involved entities in the relation object.
To be precise, your ER model is like this
Exercise (id, name)
Question (id, wording)
ExerciseQuestion (exercise_id, question_id, order) // both ids
// form primary key
Alternative (id, text, question_id)
And your classes should follow that.
I'm struggling with this kind of issue and I can't find direct answer to my question through Google.
Let's say we have a table 'users' in the database and it has the following columns: id, username, password, real_name.
Then my problems starts after that.
I know we can make an independent class for that like:
class User
{
private $_id;
private $_username;
private $_password;
private $_real_name;
// getters
// setters
}
Now, should I add functions like fetchById on that class? I mean, is it a good practice?
public function fetchById($id)
{
// validate param
// query database
// copy results to appropriate properties
}
Or should it be done by another class, like UserManager? Then for every result, we convert the array result to of that object?
Also, where should I put functions like fetchUsers where it will fetch multiple users from the database? Functions which deals with multiple records of the same entity.
I am looking for code efficiency and performance.
I know 'some' ideas but I can't justify it. I need help.
I'm currently working with CodeIgniter but I think this problem is PHP OOP in general.
For me personally, I have my models (objects that represent database tables) extend an abstractModel object that has the ID attirbute and shared static functions like fetchById(). This abstract model also has methods like save() which use the ID of the object to save.
You don't have to have an 'id' field in the table, the id of the model just has to be one of the unique key fields in the table.
Instead of fetchUsers() I have a generic loadAll() static function in the abstract class. Thus you could call Users::loadAll() to get all the models of your users. This means that most of your models can be interfaced with in the same way and reduces duplication of code. Of course if there are methods specific to the model, then you will need to define them in child model.
Build a class for table management and another for entity.
see other ORMs like doctrine, propel, or frameworks ORM like cakephp.
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.
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!