Doctrine 1.2: Add properties to a relationship

Doctrine 1.2: Add properties to a relationship

I recentry had the need to had properties to a relationship using Doctrine. In fact this use case is not taken care of by the ORM (and ORMs in general) and there is no really good solution. Nevertheless, I found a not so bad way to implement such a pattern.

Here is what I’ll do:

As you can see, a very common school example.

We can already describe our objects and the N to N relationship.

[yml]
# Shema.yml
Student:
columns:
id:
type: integer
primary: true
autoincrement: true
name:
type: string
relations:
Courses:
class: Course
local: student_id
foreign: course_id
foreignAlias: Students
refClass: Enrolment

Course:
columns:
id:
type: integer
primary: true
autoincrement: true
name:
type: string

Enrolment:
columns:
student_id:
type: integer
primary: true
course_id:
type: integer
primary: true
[/yml]

In can now load the Doctrine task Doctrine_Core::generateModelsFromYaml to generate my model.

A good way to see I the generation went well is to open the generated base files and read the class comments. Let’s have a look:

[php]
/**
* BaseCourse
*
* @property integer $id
* @property string $name
* @property Doctrine_Collection $Students
*/
abstract class BaseCourse extends Doctrine_Record
{
// …
}

/**
* BaseEnrolment
*
* @property integer $student_id
* @property integer $course_id
*/
abstract class BaseEnrolment extends Doctrine_Record
{
// …
}

/**
* BaseStudent
*
* @property integer $id
* @property string $name
* @property Doctrine_Collection $Courses
*/
abstract class BaseStudent extends Doctrine_Record
{
// …
}
[/php]

You can see that all my classes have been load with the right properties. The interestig part here is to see that the student and Course classes is agnostic from the Enrolment class. Thus the couse already has a collection of students and the student a collection of courses.

So we have here a perfectly working N to N relationship. Doctrine takes care it self of the join operation when accessing the database.

Now, let’s had our properties on the enrolment relationship.

The idea here is to provide Doctrine a way to acces the enrolment class. The most simplier way to do that is to add the following relationships to the enrolment definition:

[yml]
Enrolment:
columns:
student_id:
type: integer
primary: true
course_id:
type: integer
primary: true
enrolementDate:
type: date
notnull: true
relations:
Course:
local: course_id
foreign: id
foreignAlias: Enrolments
Student:
local: student_id
foreign: id
foreignAlias: Enrolments
[/yml]

You can notice that I added the One to N relationships and the enrolmentDate property.

After a class generation we obtain :

[php]
/**
* BaseCourse
*
* @property integer $id
* @property string $name
* @property Doctrine_Collection $Students
* @property Doctrine_Collection $Enrolments
*/
abstract class BaseCourse extends Doctrine_Record
{
// …
}

/**
* BaseEnrolment
*
* @property integer $student_id
* @property integer $course_id
* @property date $enrolmentDate
* @property Course $Course
* @property Student $Student
*/
abstract class BaseEnrolment extends Doctrine_Record
{
// …
}

/**
* BaseStudent
*
* @property integer $id
* @property string $name
* @property Doctrine_Collection $Courses
* @property Doctrine_Collection $Enrolments
*/
abstract class BaseStudent extends Doctrine_Record
{
// …
}
[/php]

I now have the additionnal relationships and I can access my relationship class which has a new property: enrolementDate.

But, such a schem force us to change some way of working with the model.

Working with the model

The first thing that we need to keep in mind, is that if we add a student to a course or a course to a student by using the Course::Students and the Student::Courses collections, Doctrine will autogenerate the Enrolement object and save it. Thus we won’t have access to the enrolementDate field.

So instead of using the common way of adding element to a collection we should instantiate a Enrolement object, associate it with the course and student, and then save it. During this process we so have access to the enrolementDate.

[php]
$student = new Student();
$student->name = "Michel";
$student->save();

$course = new Course();
$course->name = "Programming 101";
$course->save();

//create the relationship
$enrolment = new Enrolment();
$enrolment->enrolmentDate = date(‘c’);
$enrolment->Course = $course;
$enrolment->Student = $student;
$enrolment->save();
[/php]

After that, I have a correct set of data in my database. I can also do some access:

[php]
//retrieve the courses
$courses = $student->Courses;

//retrieve the enrolments
$enrolments = $student->Enrolments;

//retrieve the course passing through and enrolment
$course = $student->Enrolments[0]->Course;
[/php]

The only matter is that I cannot access an enrolment given a student and a course.

Here is what I did to pass this issue. I simply added a function to my Student class that will generate the

[php]
class Student extends BaseStudent
{
public function getEnrolmentForCourse($course){
return Doctrine::getTable(‘Enrolment’)
->findOneByCourseIdAndStudentId(
$course->id,
$this->id
);
}
}
[/php]

As you can see, I simply use a magic function of doctrine. Even if the findOneByCourseIdAndStudentId function does not exist, Doctrine uses its name to generate the appropriate SQL query; I just need to fill the blanks with the ids of both objects.

Now I have a complete access to my enrolment object:
[php]
// find Michel
$student = Doctrine::getTable(‘Student’)->findOneByName(‘Michel’);
// take his first course
$course = $student->Courses[0];

// retrieve the enrolment data from it
$enrolment = $student->getEnrolmentForCourse($course);
[/php]

We saw here how to implement a full access to a relationship containing properties. It involves some schema description and a simple additional method in the classes’ code, but it imposes a specific way to create N to N relations. Thus it is not perfect; I hope this solution will help you working better with Doctrine.

Extended customization of jQuery UI Autocomplete

Extended customization of jQuery UI Autocomplete

I recently, spend quite a few time looking into a brand new jQuery UI widget for extensibility point in order to get a real customization.

As usual, the widget comes with a various panel of options and events, but I really wanted uncommon behaviors.

Use case

On my new website I can associate tags to my business object. I already implemented an indexer in order to retrieve tags from a simple AJAX query, I just want to add a fancy UI. Thus I added a simple text input to my form on which I want to bind my indexer via jQuery UI Autocomplete.

Here is my html:

[html]
<input type="text" id="tag-input">
<div id="tag-list">
<span>Microsoft<input type="hidden" value="1" name="object[tags_list][]"></span>
</div>
[/html]

You can see here that I placed a text input that I will “jQueryUIze” and a div that will hold my selected tags. I already put one tag into it so that you can see exactly what DOM structure I need to correctly post my form. The tag is represented using a hidden input and the held value is the Id of my tag.

A simple first implementation of the jQuery widget could be:

[js]
$("#tag-input").autocomplete({
minLength: 3,
source: function(request, response) {
$.ajax({
url: "/tag/suggestion.json",
dataType: "json",
data: { query: request.term }
});
}
});
[/js]

Here I just implemented some built-in features of the widget. When I type more than 3 characters, an AJAX request is made to my server. For example, when I type “Micro” this url is requested: /tag/suggestion.json?query=micro.

Now let’s override it !

Apply a specific data mapping

First, you need to remember that I want to share more than a simple label because I need the Id in order to correctly add it into my tag list. Also, I would like to have the requester term set to bold into my suggestion list.

Thus, here is the returned json:

[js]
{"tags":[
{
"id":"1",
"label":"Microsoft"
}
]}
[/js]

In order to correctly use the json data I need to add two modifications. First I am going to map the ajax result and then edit the renderer of the widget:

[js]
$("#tag-input").autocomplete({

//…

$.ajax({
url: "/tag/suggestion.json",
dataType: "json",
data: { query: request.term },
// 1rst need a specific mapping from my json data
success: function( data ) {
response($.map(data.tags, function(item) {
//this regex is for the bold of the suggestion list
var label = item.label.toString().replace(new RegExp(‘(‘+request.term+’)’,’i’),'<strong>$1</strong>’);
return {
// for each tag, I will retrieve a object with these 3 properties
id: item.id,
label: label,
name: item.label
}
}))
}
});

//…

//this 2nd modification is for the rendering of the suggestion list
}).data( "autocomplete" )._renderItem = function( ul, item ) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( ‘<a href="#"><span class="tag">’ + item.label + "</span></a>" )
.appendTo( ul );
};
[/js]

You can already notice the two modifications. The first one is to translate every tag from my json into an object composed of three properties: the id for the form submission, the label for the suggestion list and the name for the form display.

The second modification overrides the item renderer. For each item it need to render, it simply creates a list item (li) on witch I add the item (we will use it later), append the proper layout (a link with a span and the label), and then add it to the suggestion list (ul).

Now I have the rendering that I expected!

After customization

Customize behaviors

Now, I want that when I select an item it flushes my search and append the selected tag to my tag list. In order to pass through this issue I will simply use the select event of the Autocomplete widget:

[js]
// when a element of the suggestion list is selected
select: function(event, ui) {
var canAppend = true;
//crawl the current tags to see if I can append it or not
$(‘#sticker-list .sticker input’).each(function(){
if($(this).val() == ui.item.id)
canAppend = false;
});
//add the tag to the tag list
if(canAppend)
$(‘#tag-list’).append(‘<span class="tag">’+ui.item.name+'<input type="hidden" name="object[tags_list][]" value="’+ui.item.id+’"></span>’);
//clean the value of the input
$(this).val("");
//return false to prevent update of the input
return false;
}
[/js]

The select event is called every time a item from the suggestion list is selected. When it occurs, I simply need to know that ui.item contains the item that constructed via the costom mapping (1rst modification) and passed trough the custom rendering to my DOM element (2nd modificcation). Now, I can check if the tag is not already in my tag list and append it.
The last thing to do is to empty the text input and prevent the default behaviour that will fill the input with the selected item. In order to do that I just nedd not to forget to return false.

And voila! We made a complete customization of the jQuery UI Autocomplete widget.

You can download the full example here : Autocomplete Customization.

About me…

As you know, I am a student in ECE Paris. It is an engineer school with a 5 year program. I am going through the last year and I took a specialization in software development and security. My courses also contain some management, software design, user experience, marketing and other useful skills.

I also enjoyed traveling during my studies. I went for a full semester in Canada where I discovered new techniques and way to approach management and software development issues. Also I am currently packing for a seminar in California which will last for a month.

About my skills, I developed a lot for web applications with two main platforms. I have a personal big project on a community web site which I am developing with some friends of mine. It is based on the symphony framework with the Doctrine ORM. I was already familiar with web development in php but this framework made me go on a whole new scale. I took advantage of this project to put into practice Extreme Programming which was a really good experience, not easy at all but definitely interesting.

Also, I am currently finishing an internship that I voluntary spend on a complete different technology: .NET. At the beginning I wasn’t used to the Microsoft platform but I learned and I finally took part in the development as regular member of the team. We are developing a fully configurable web application designed for HR services that should fit the need of many customers. It is a very complex project based on several key technologies of the .NET framework such as asp.mvc, entity framework and unity. The architecture that we are developing also challenges major issues of nowadays developments. Again another agile methodology, we are practicing scrum.

In my personal life I also enjoy playing the guitar, movies, and reading. Well, I just don’t like getting bored.

Welcome to my new blog !

Hi, welcome to my new blog!

Until recently I kept developing my own website based on the symphony framework but I realized that I didn’t have enough time to make it as good as I wanted. Also, with my current internship over the .NET framework and my personal big project on symphony I was really starting to become schizophrenic.

I was considering starting a real pro-blogging activity thus I decided to switch to a real tool: WordPress. Less time loosing reinventing the wheel, more time to share my experience. Some of you might notice that my old work is still available at perso.michelsalib.com.

With this blog, I intend to share on various subjects over several technologies. Symfony, of course, Doctrine, jQuery, but also Microsoft technologies such as asp.mvc, entity, and I hope Silverlight and WM7 development…

Yea, I am schizophrenic!