Nerds who love the symfony-project
23 Dec
I’ve been thinking about the fact that the engineers at Google view static classes as terrible things. My own code has an unfortunate number of static classes. Mostly this is because I am not sure where to put some of the code. For instance, on every project I have a few methods that format output. Some of these methods are the same on every project, so I hate to put them in the action code. Where else? Output formatting obviously does not belong in the model classes. What about methods for formatting dates, methods which I use in action code in multiple modules? It is easiest to put this into a class of static methods. Then I can call it from everywhere.
Another approach would be to add methods to the parent class. The parent of all action classes is sfActions. I’ve thought about editing sfActions, but I hate the idea of editing a core class of Symfony. I’ve also thought about creating a child class of sfActions, adding my methods there, and then having all of my action classes inherit from that. This is probably the best way to go, but I admit to a certain laziness. At some point I plan to write my own custom generation classes, which would make this easier. For now I tend to rely on the auto-generate code. Also, there are some good arguments against tall object trees. In his book Effective Java, Joshua Bloch suggests “Favor composition over inheritance.”
Given these concerns, I was interested to read this article about worker classes in Symfony. They list these advantages:
Advantages of worker classes
1. actions could be shrinked huge in size
2. worker classes are more decoupled from the rest of symfony and could be tested better
3. you can replace the content of an worker method without breaking actions
This is a step away from the problems of static classes. It provides a place in the object hierarchy where this code might legitimately belong. The engineers at Google suggested that most static methods should really belong to one of their own parameters. I can imagine worker classes as a way of making this happening, without bloating up the owning class.
A follow-up article looks at the interface changes that worker classes allow. These changes should allow better long-term maintainability and better testing. I especially like the idea of removing all ORM specific code from my action classes.
The action (or template) does not process anymore with a concrete ORM model. With the help of the proxy class, we can change the underlying model layer without breaking anything in the action. By providing a well defined interface, the proxy class manages how a model could be modified and accessed.
At this point, you should understand the reason for all those changes. It may look like a huge overhead for just retrieving some data out of the model. But in our case it was a precondition for our global architecture.
Our models and our main business logic is partly served by and processed in SOA applications (there we use Propel and i hope in some near days also Doctrine). I implemented a mechanism, which allows worker classes to access those SOA applications very easily. They are fetching the serialized data from those applications, hydrate them into proxy classes and provide them to the rest of the project.
…In this way, action code is really short, it is completely decoupled from the logic how the model is originally handled and it works quite well. If we decide to change something in our architecture some day, we only have to change our worker and proxy classes. The rest of the project will not notice those changes, as it should be in a layered architecture.
Sure, this step is nothing for small applications. But when your project grows and grows, the number of requests in a second turns from dozens to hundreds, then you’ll be thankful, if your project provides this approach.
I will probably be adopting some of this technique for my own current project.
6 Mar
If you are defining your schema.yml and have a field that is a decimal value, you will find you will need to somehow pass through the precision (maximum number of digits to the left of the point) and the scale (total number after the decimal point). The documentation for the syntax of this is quite poor.
For all those that want to know, in Symfony 1.0 this is how you would define a decimal:
my_decimal: { type: decimal, size: 8,3 }
For Symfony 1.1+, the Syntax is a bit different:
my_decimal:
type: decimal
size: 8
scale: 3
Where 8 is the precision and 3 is the scale. Hope this helps.
6 Mar
Symfony application configuration can be configured for each application in the app.yml file. One of they key problems with this is if you want an end-user to be able to change some of this application configuration, they can’t. You would need to do that for them.
A good way to fix this is to store your applications configuration in the database. This comes with a performance cost (extra calls to your database) - so its up to you to decide if this is the most efficient solution for you.
Here is an outline on how to get started building your application’s config in the database.
Step 1: Define your model
Create the columns you want to store for your application config. In this instance, we are just going to store name and value pairs (ie Attribute & Value). Here is a sample below:
propel: #Application configuration table my_app_config: _attributes: { phpName: MyAppConfig } id: { type: integer, required: true, primaryKey: true, autoIncrement: true } attribute: { type: varchar(255) } value: { type: varchar(255) } ...
Once you have finished that, go ahead and build your model
Step 2: Extend Your MyAppConfig class
So the next step is for us to make it easy to access application configuration. For us to do this, go to your lib/model/MyAppConfig.class.php and extend your base class as follows:
<?php class MyAppConfig extends BaseMyAppConfig { /** * Returns a value of an attribute string for my app's config table * @param string attribute name * @return string The value of that attribute * @author eHabib */ public function lookupValueFromAttribute($attribute){ $c = new Criteria(); $c->add(MyAppConfigPeer::ATTRIBUTE, $attribute); $result = new MyAppConfig(); $result = MyAppConfig::doSelectOne($c); return ($result->getValue()); } }
Step 3: Use it!
Let’s say that in our database we have the following attribute/value paris:
Attribute: app_support_email, Value: myemail@mydomain.com
Attribute: app_base_url, Value: http://whatever
You could access this data in your code by doing this:
$myConfig = new MyAppConfig(); $appEmail = $smyConfig->lookupValueFromAttribute("app_support_email"); $appURL = $smyConfig->lookupValueFromAttribute("app_base_url"); ... echo $appEmail.",".$appURL; //lets print it
That’s it!
Step 4: Configure it via your front-end
The last step (if you wish) is to allow the end-user to update the value of these fields. To do this, you can build a form to update these fields, or use Symfony’s admin generator.
6 Aug
I am working on an interesting project where I have been given the task of creating a web interface that allows an authenticated and authorised user to conduct specific types of search on a huge data set.
The data set begins as flat text logs which are transformed and loaded into a normalised schema - which was designed well before I got involved. So, I have been very impressed by the way that propel and creole have consistently reverse engineered the schema and built a reliable model.
This is not a post about Propel or Creole but I thought to put this into context since I have been given a development specification that requires me to present data on a browser in a manner different to the way in which it is ordered and layed out in the database schema.
So - consider a table that has the following structure:
id (auto increment unique id) my_date (of type date) my_date_as_int (a conversion of date to int eg 2008-08-06 would be 20080806)
The insertion of dates into the above fields is not ordered - so id 1 may have an older date than id 2 and so on…for example:
| id | my_date_as_int | my_date |
| 1 | 20080602 | 2008-06-02 |
| 2 | 20080724 | 2008-07-24 |
| 3 | 20080723 | 2008-07-23 |
| 4 | 20080411 | 2008-04-11 |
| 5 | 20080412 | 2008-04-12 |
My task was to create a “drop down” select tag that showed the above sorted in an ascending order by the date field.
Step 1. Create a __toString method to return the date field as opposed to the Id.
public function __toString() { return $this->getMyDate(); }
Step 2. Create a custom method in the relevant Peer class to get a sorted list:
public static function getSortedDate() { $c = new Criteria(); $c->addAscendingOrderByColumn(DatePeer::MY_DATE_AS_INT); $rs = DatePeer::doSelect($c); return $rs; }
As you can see, I choose to sort by the integer representation of the date field, but you can use the MY_DATE field in the same manner.
Step 3. Create a drop down select_tag in your view.
There are two options here.
Option 1. Standard form name, value pairs being the date and id fields respectivey.
<?php echo object_select_tag('', 'getMyDate', array ( 'related_class' => 'Date', 'peer_method' => 'getSortedDate', 'include_blank' => true, )) ?>
Option 2. Form name, value pairs being the my_date and the my_date_as_int fields respectivey.
<?php echo select_tag('', objects_for_select(DatePeer::getSortedDate(), 'getMyDateAsInt','getMyDate'), array('id'=>"this_date",'name'=>"this_date")) ?>
That is it! The view then renders a drop down select that is constructed firstly by a call to the getSortedDate method, which in turn returns an array of sorted items - each of which is shown in the drop down using the __toString method to convert from an id to the date.
25 Jul
If you are writing your schema.yml file, the Symfony book isn’t too detailed as to what you can and can’t put in your schema.yml. I’d recommend you head over to the Propel Guide, and read the appendix for detailed documentation.
25 Jun
Symfony has an awesome feature that automatically re-populates your form for you without writing any code. All you have to do is specify it in your app/module/validate/myForm.yml file:
fillin: enabled: true
So what happens if you have multiple forms on the same page? Well all you have to do is ensure that each one of your forms has ID and a Name:
echo form_tag('mymodule/action', 'name=myForm id=myForm');
Then in your yml validation file for that form, ensure that you specify the form name:
fillin: enabled: true param: name: myForm ....
That’s it. Click here to read more about Symfony Form Validation.
24 Jun
Wow, I’m blown away with how easy it is to get some pagination going with Symfony. In my previous life, I had coded my own custom Pagination class - it would have been easily 100 lines of code. I’ve just implemented some very basic pagination with less than 20 lines of code.
I’m assuming you have created your model objects, and have a basic setup going. Im also doing this in Symfony 1.0. Lets now go ahead and create some basic pagination.
1. Edit your actions.class.php for the module you want to paginate. In my case, I want to create a listing of all companies. For this, I’ll have a executeList action within that class. Here is what it looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class companyActions extends sfActions { ... public function executeList(){ //Get a listing of all companies $this->companys = CompanyPeer::doSelect(new Criteria()); //Lets do the pagination for it, firstly define a pager for the Company object. Lets go with 10 results per page $pager = new sfPropelPager('Company', 10); //Define the criteria, in this case, we won't have any - lets get all companies $pager->setCriteria(new Criteria()); //Set the first page to 1 $pager->setPage($this->getRequestParameter('page', 1)); $pager->init(); $this->pager = $pager; } ... }//class |
2. Now lets modify your view for listSuccess.php to display what results you are viewing
1 2 | //Display a summary of the total companies found, and how many companies there are echo $pager->getNbResults()." Companies found. Displaying results ".$pager->getFirstIndice()." to ".$pager->getLastIndice(); |
3. Now lets put in our main code to loop through the paginated results (still in listSucess.php)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | foreach ($pager->getResults() as $company){ echo $company->getCompanyName(); //go through and display all you want maybe in a nice table here //... } //Now lets display a small navigation bar with arrows for next, previous and page numbers echo "<br/>"; <?php if ($pager->haveToPaginate()): ?> <?php echo link_to('«', 'company/list?page='.$pager->getFirstPage()) ?> <?php echo link_to('<', 'company/list?page='.$pager->getPreviousPage()) ?> <?php $links = $pager->getLinks(); foreach ($links as $page): ?> <?php echo ($page == $pager->getPage()) ? $page : link_to($page, 'company/list?page='.$page) ?> <?php if ($page != $pager->getCurrentMaxLink()): ?> - <?php endif ?> <?php endforeach ?> <?php echo link_to('>', 'company/list?page='.$pager->getNextPage()) ?> <?php echo link_to('»', 'company/list?page='.$pager->getLastPage()) ?> <?php endif ?> |
That’s it! Now have a play with that, and if you are gaim, you could even go and do some Ajax Pagination or get some sorting going.
24 Jun
One of the great features of symfony is of course that you can easily make a change to your model by simply updating your schema.yml file then doing a propel-build-model
The really nice part is that all the extensions you put into your model, if writen to the model extension files in /lib/model will still be there, and only the base classes in /lib/model/om are updated.
But everyone knows this, so what is the tip!? Well, if you completely remove a table from your DB, and hence from your model, you will need to manually delete the files in both /lib/model and /lib/model/om. It seems propel will add new files, modify existing, but not delete those removed from your model. And that, is your symfony tip for today.