Zend Framework, subjective experience

Recently I was asked to develop a web application. I won't go into details, but only say that the application related to transportation planning. There is a public part, which can take advantage of any visitor. There are internal interfaces for the system operators. There are widgets to embed on other sites. From a technical point of view, it is a few dozen screens, many different forms tablets. Part of the screens use ajax, custom components, written in javascript, and all sorts of nice type, drag-and-drop. Data normally stored in a relational database in the form of a half dozen tables. Overall not a very primitive app, but very difficult to call him can't either.

At work I often have to design or personally, to code such applications. However, this project was one important requirement. The application must be developed on the basis of serious and proven platform, namely the Zend Framework. The use of bespoke “bikes” — is unacceptable. Frankly, real-world experience working with Zend Framework I still was not. But the platform is known and it is known to the developer. Many developers Zend Framework is generally regarded as the standard for web development. So, the more there is a reason to learn something new and solid. So I enthusiastically took up this project.

Next goes the description of my personal emotions and impressions, so perhaps they should not be taken too close. This is a kind of emotional residue remaining after completion of the project. Zend Framework in commercial project was used by me for the first time.


Zend Framework is positioned as a great, well thought out and user friendly platform for developing web applications. What is a typical web application in my personal opinion? Well, that's a lot of different screens, many forms, many plates. All of this is actively working with the database, typically a relational database. Okay, I will, more specifically, in 95% of cases with MySQL. So, from a development platform for web applications, I at least expect good possibilities for creating forms, different screens and comfortable working with a relational database. And so I started to learn.

The first thing I needed to learn the basics of the Zend Framework. I had read the book on this platform “Zend Framework. Developing web applications in PHP. Author: Vikram Vaswani”. It seems that at the moment it is almost the only Russian-language book in print devoted to such frameworks. In principle, the book is quite good. After reading, I had a General understanding of how to work with the framework. Then I began to study the official documentation to fill in the gaps and in General to understand, what else can I provide this framework. After all, the book considers not all the features of the engine. Comprehensive and lucidly written official documentation is here framework.zend.com/manual/ru About a week later I decided to proceed to the development application.

My world of coding


A few words about my personal opinion on how should be written the products. I will mention here and those things for which I am sometimes criticized.

I respect OOP and apply OOP Paterno. But the use must be reasonable and actually make it easier for code readability or further improvement. Paterna for Paterno — it's bad.

Good code is clear code. Understandable code is a simple code. If you can write code in short, as a rule, it will be clearer. The more logically related pieces of code fit on one screen of the monitor the better.

If I'm not satisfied with something in already existing solutions and approaches, I'm thinking of creating your own solution that will satisfy me more. Yes, many call it “reinventing the wheel”. But I prefer to create a comfortable bike, instead of long and sadly finished with a file to someone else, it's not really a good solution.
MVC is great, but not in each specific case simplifies and accelerates the development process. In some cases, it is possible to retreat, especially when we are talking about simple actions, but these actions very much. (But I'm in no case do not want to say that MVC is not needed at all.) Here is an example:

the
if(@$_POST[‘action’] == 'done') {
$dbi- > exec(“UPDATE bug SET status=’done’ WHERE id=:bugId“, array(‘bugId’= > $bugid));
$message = “Status changed to <b>Done</b>”;
}
elseif(@$_POST[‘action’] == ‘delete’) {
/ / ....
}
/ / .... plus 20-30 other simple actions...


Yes, I here mixed Model, Controller, router and View. It looks like a snippet of bad code. However, in some cases, lucid, I will write so, if this is justified from the point of view of time costs, the amount of code and the possibility of further support. In General, I am against ardent mvc-fanaticism, and I'm for common sense and thoughtfulness approaches.

In the project referred to in the article, of course, such code not. I don't think I have enough experience to consciously deviate from the approach dictated in Zend Framwork, so I tried to follow his concept.

Now a little about templates in web projects. I don't understand when using template languages such as Smarty. Well, why invent a new and in General is limited to language when there's a full-fledged language PHP? Did the coder hard to learn to use PHP-shnye if(...) foreach(...)? It's not more complicated than Smarty, but we do not add a new exotic language.
By the way, is that the templates (or Views) are so complex that they have to use the if and foreach, and a very complex structure, including classes and objects that inherit from each other. Although, the task of repose remains the same — to generate the HTML code for dry data.

In General, for templates I always prefer the usual PHP, and the promise of the developer not putting in the template there is no logic. For processing template, you can use a simple type function

the
function renderTemplate($file, $vars) {
foreach($vars as $_name = > $_var) {
$$_name = $_var; 
}
require $file;
}

// Example 
renderTemplate(“page.tpl.php”, array(
‘menu’ => array(‘/about’=>’About us’, ‘/contacts’=>’Contacts’)
‘title’ => ‘Title’
));


Plus of course the simplest tying to escape, for caching. Like nothing more is necessary for happiness.

Perhaps I still have nothing against XSLT as template language. But only for cases when the information is not very much and they are relatively simple. Well not do you easy on the bare XSLT template for some hellish form, structure and geometry of which depends entirely on the received data. It will require, and loops and objects and polymorphism and inheritance.

Installing Zend Framework


Freimark, as it turned out weighs about 25MB and contains nearly 3000 files. Pretty good, I thought! There probably sold everything you could wish for!

To begin with I was a little unhappy with the directory structure of the application based on the Zend Framework. It involves a very large number of directories. When working on a project I'm constantly lost in this structure. Also a bit premodernity I thought rules the names of the generated classes, rules for naming files and ways of their placement. The logic they have, but honestly, all this could be made more intuitive. And very tiring journey through the directories of the scripts view. Although, apparently if you long to work with this framework, you will get used to it.

But I was very tired the ritual of creating new sections of the app: Directory created, file name, class name, the controller name. First do this with a controller then to view. Yes, there is a command line utility, which in some cases allows to do it automatically. But in some cases she is incapacitated.

It turned out that Zend Framework is not designed to work with shared hosting, because to run it requires to register in VirtualHost directory other than the root of your project. So, you must have at least VDS. Yeah, I've heard the opinion that if you develop something that works on a shared hosting, then you are not cool, and you have no place among professionals. Although I have always believed that 95% of all web projects spinning it on shared hosting. Well, in any case, after a few strokes of a file, I forced the project to climb the shard hotinga.

First I needed to create a data model (in the framework of the concept MVC). As it turned out, the Zend Framework does not provide anything to create models. Essentially you just write your own class whose methods are to perform a particular operation on the database. Ie in some cases, the model class is just a wrapper over a SQL query, and sometimes SQL queries with some additional piping to PHP.

To work with the database the framework uses the Zend_Db components. It allows you to work with most common DBMSs using SQL.

It also allows you to make queries without using SQL, and designing a cunning object that is inside the framework still developing in SQL. That's just for me for writing more or less complex query, it is still more convenient and easier to write a SQL query, than to construct this wrapper, which, as it turns out, allows you to create not any complex query. In General, I think that it's better to learn SQL myself than trying to force it to form a framework, and hope that he will do it in an optimal way. Native SQL is much more readable, understandable to all programmers and allows to use all the features of a particular DBMS. You say that you use the object query designer allows you to ignore the dialect of the specific database? For complex queries — it is not always possible to ignore, even with a cool design. Well, another argument I don't remember a single project where you would need to change the DBMS, and thus is not supposed to a global alteration of the whole project, the business logic and data models, i.e. writing a completely new project based on the previous one. The preservation of a piece of data model class from the previous version of the General background does not much easier life.

So, it looks like working with SQL in Zend Framework? Well, something like this:
$result = $db->fetchAssoc('SELECT * FROM items WHERE age>18');

And then since $result can be used as an associative array in the form of a table (even though it was not an array), which is very convenient. You can pass an array for further processing, or you can directly pass it to the view for display.

There are several convenient methods that retrieve the results of SQL queries:
fetchCol() — fetches a single column. Convenient when you need to get a list of ID-NIS
fetchRow() — fetches the first row. Convenient when you have to choose one record by primary key.
fetchOne() — Fetches only one value. Also convenient when you really need to get one value and do not want to write code for picking a single element from an array.
fetchPairs() — fetches the value pairs in an associative array. Creates a hash of key-value. Very useful to create a different dictionary.

All of these methods allow the little things to reduce the number of lines of code. To remove all sorts of additional cycles, fetching items, etc. In the end, obyi the amount of code in the models is markedly reduced.

Method fetchPairs() is good. Now, how to get a little more interesting and need a dictionary, where the key is the primary key of the database, and the values of the array record database? A very common task. The answer is a no! Do hands, call the fetchAssoc(), then twist the loop and generate a new array-a dictionary. It's a shame!

Okay, go ahead. It is necessary to extract from the data table and to construct of them a tree array. Is this common operation is also not implemented. I.e. there is nothing like:

$hierarhy = db->fetch_tree(“SELECT id, parent_id, title FROM tree‘, ‘id’, ‘parent_id’);

Go ahead. In SQL you need to pass the parameters. Well, Yes, at first glance, everything is simple. Here's how:

$result = $db->fetchAssoc('SELECT * FROM news WHERE id = ?', 7);

You can even several plasmodial to deliver.

$result = $db->fetchAssoc('SELECT * FROM news WHERE chapter=? AND type=?', array(2, 8));

Only here when the program become complex, and the complexity of queries grows, and the parameters can be dozens. Need named placeholders. To count the placeholders by their numbers — is not conducive to readability of the code. As it turned out, the Zend Framework does not implement them! They work, but only if the database supports them. For example, if you chose MySQLi then named placeholdername you can not work. Was it really so hard to make emulation of named parameters?! Okay, this thing was solved. It turns out if the adapter you choose and Pdo_Mysql instead of Mysqli, then the named parameters will work, because they are emulated within the PDO. I must confess I came to it immediately. Resentment has abated, but left an unpleasant aftertaste.
Often SQL queries have to use code like this

SELECT * FROM items WHERE id IN(1,3,5,8,12)

where the list of id-NIS, is dynamically generated from an array.
I personally have not found a way to do it on Zend Framework nicely. There is a method quoteInto(). You can use this

$sql = $db- > quoteInto("SELECT * FROM item WHERE id=IN(?)", array(1,3,5,8,12)));

Accept the inability to use named parameters in the query with the preparation of a complex query from several pieces, but getting SQL code. Only we still get syntaxical an error if the array is empty, because the SQL to get a as SELECT * FROM item WHERE id=IN().
So either you wrap everything in if(count($idList) > 0) or to the array each time we add a deliberately invalid ID, such as -1. Kriven'ko, but the solution. But still, it turns out? to build a complex SQL query is only possible by the concatenation of the strings. But I prefer to see any SQL query as a single piece of code, without any blotches PHP. And damn it, mixing HTML and PHP is considered bad, but SQL and PHP, to see whether, normally.

In General, not much I pornville Zend_Db. The biggest benefit or convenience of its use I have not noticed. By the way Zend_Db weighs more than 500KB.

In General, for work with databases I prefer to use homemade small class with the following public methods:

connect($config) — Connects to database
fetchTable($sql, $params) — Returns an array of records
fetchRow($sql, $params) — Returns a single record
fetchCol($sql, $params) — Returns a single column
fetchOne($sql, $params) — Returns scalarname
fetchPairs($sql, $key, $value, $params) — Returns an array of key — value
fetchDict($sql, $key, $params) — Returns an array of key — record
fetchTree($sql, $key, $parent, $params) — Returns the tree
exec($sql, $params) — Executes select query.
getInsertId () Returns the last generated auto-increment
getAffectedRows () Returns the number of records affected

The results of the implementation — the usual associative arrays logical for each of the cases structure. The parameters are always named, you can pass numbers, strings, and lists. The class throws exceptions if a SQL syntax error. This “bike” weighs in the area of 10KB, and frankly I always have enough. Other programmers on the study required 10 minutes, and to explore the Zend_Db — hours of careful studying of the manuals, and questions still remain. By extending the class and overriding a few virtual methods, you can add support for any other database.

Forms


The next important thing for web apps is forms. They are usually quite a lot. Structure and appearance are also very diverse. To work with forms in Zend Framework Zend_Form is used.

To create a form we need to construct a form object. It is necessary to add objects of each of the fields. In the fields you can configure different parameters. To specify validators, etc. Here is an example of creating a field

the
$username = new Zend_Form_Element_Text('username');
$username
->addValidator('alnum')
->addValidator('regex', false, array('/^[a-z]+/'))
->addValidator('stringLength', false, array(6, 20))
- >setRequired(true)
->addFilter('StringToLower');


Creating all the fields we add them to the form object

the
$form
->setAction('/somepath')
->setMethod('post') 
->addElement($name)
->addElement($company)
->addElement($email)
->addElement($phone)
->addElement($action);


But when field is a lot we be sure what some of them forget to add to the form. Well, not like I am mad when in really need something to list, and then somewhere in another place again it's all re-enumerate. And here it is necessary to specify the variable name of the object field and the name to output
If addElement() was not returning a reference to the form, and a reference to the added element, using the fluent interface code would have left a much more concise and without repetition. Something

the
$form->addElement(new Zend_Form_Element_Text('username'))
- >setRequired(true)
->addValidator('regex', false, array('/^[a-z]+/'));


But you can't!

As you have noticed, then you can use validators for the fields. Well. Let's try to make the field for entering e-mail and we introduce the curve e-mail address. For this we use a standard validator EmailAddress.

Try to specify the curve email “xxx”. Get the message about incorrectness:

'xxx' is no valid email address in the basic format local-part@hostname

Okay. Of course it would be necessary to translate the message into Russian, but there it can be easy to do. Now let's try the more crooked address “я@_domain.xx”.
And that's what we get:

'_domain.xx' is no valid hostname for email address 'я@_domain.xx'
'_domain.xx' appears to be a DNS hostname but cannot match TLD against known list
'_domain.xx' does not appear to be a valid local network name
'I' can not be matched against dot-atom format
'I' can not be matched against quoted-string format
'I' is no valid local part for email address 'я@_domain.xx'


6 messages! Who cares? Does the average user actually understand what is TLD, quoted-string or dot-atom? The average user only needs a single message — “Invalid E-mail” and all! But standard tools won't allow it. To get rid of the output of this heap of messages — are invited to write your own validator class.

Well, let's say we did the form, set up all field, all the validators. Now get her out. By default, the HTML form code will look something like this:

the
<dl class="zend_form">
<dt id="phone-label">
<label for="phone" class="required">Phone</label>
</dt>
<dd id="phone-element">
<input type="text" name="phone" id="phone" value="">
<ul class="errors"><li>Enter a value</li></ul>
</dd>
...
</dl>


And DD elements will be wrapped even hidden fields! And then we begin to wonder what strange voids formed in the form. In General intelligence for special handling of hidden fields in the framework.

What if I DL a simple list is not satisfied? Suddenly I want to make a form with a complex structure? Here it is proposed to use class decorators. Decorators, these are classes which allow to wrap the form elements in HTML certain tags, for example, wrap element not in tag DT, and for example in DIV. Oh the horror! “Proper” MVC framework forces us to do the layout directly inside the controller, and the most unreadable way!

In my opinion, imposition of non-standard forms may only be coder. He should just do a view script which contains all the layout of the form. And to use a dozen classes of decorators inside the controller is, to put it mildly, crookedly.

Let me give an example of a simple “bike” to work with forms, which allows simple and convenient to create forms.

The configuration of the form:

the
$form = new UniversalForm(array( 
'name' => array('label'=>'Name', 'required'=>true),
'email' => array('label'=>'Name', 'type'=>’email’),
'birthday' => array('label'=>'date of birth', 'type'=>'date'),
'sex' => array('label'=>'Gender', 'type'=>'choice', 'items'=>array('m'=>'M', 'f'=>'W')),
'code' => array('label'=>'Code', ‘regExp’= > ’/^\d{6}$/’, ‘regExpMessage’=>’Invalid code’, ‘value’=>’000000’),
'action' => array('type'=>'hidden', 'value'=>'updateInfo'), 
)); 


Here, to reduce the amount of code do ability allow instead of explicitly creating objects, form elements, simply create a configured array. Although this does not exclude the possibility of creating objects fields and validators manually. Well, then, use form:

$form->setFromPost(); // Set data from Rota
$form->isValid(); // Check that data is valid. If not, it will set error messages that will print the script view of the form.
$data = $form->getValues(); // Get value
$form->setValues($data); // Set the field values. Although it is possible to do through confiugure the array in the constructor
$form->render(‘form.tpl.php’); // Generate HTML. If you do not specify a template, the template will be used by default.
$form- > renderJs(); // Generate the JavaScript validators that duplicate PHP validators on the client side.

$form->getSqlSet(); // Returns a piece of SQL code
You can use

$sql = “UPDATE account SET “.$form->getSqlSet().” WHERE id=777”;

The following methods are used for the same purpose.
$form->getSqlFieldsList();
$form->getSqlValuesList();
Yes, it is not quite canonically correct decision. But the set of fields necessary to describe only once in the form constructor. In the case of a change it will automatically change all SQL queries.

Admin panel


Generally, for any complex web application requires the creation of some admin panels or control panels. For example to set parameters, or to edit directories, etc. i.e. at least need a tool to build editors for lists of records. At least flat lists, and for complex projects, these lists should be often hierarchical.

In General, in this framework, there is nothing ready for this. If necessary, you have to implement everything yourself manually. I.e., for each editor to create a screen with a table, form, and practically to manually implement CRUD operations.

Insights and impressions


As you understand, this “right” framework has made me a not very good impression.

But perhaps that Zend Framwork is still possible to master in the following cases:
1. If you have enough experience to make a reasonable frame for your application.
2. If you do not know how to work with MVC, but want to experience this ideology.
3. If you have a good idea about OOP and application design typical of Paterno, Zend Framwork will show you many examples. There they used the PLO wherever necessary and not necessary.
4. Also Zend Framwork it will come down, if you make a simple app that is more directed to retrieving data from a database and output to the screen. For example, some news portals or product catalogs.

Although, maybe it's better to look at any other framework? Zend Framework has already begun to lose ground, and maybe on purpose.

And personally I stumbled upon inconvenience almost all the components of the framework which was trying to work. Regularly grumbling under his breath things like “This could be done easier!” or “why they didn't implement that!”. I was under the impression that the main task of the developers of the Zend Framwork was to make all Oope. All that is possible, all wrapped in drapery, adapters, and whether it is comfortable or not. The level of abstraction in the library is extremely high, so high that it is hard to understand. And most importantly, practically any component should be finished to suit your needs. Raw things do not work out as you want.

Yes, I am well aware. Probably I misunderstood something in the framework. Probably missed something in the manuals. And now I get comments in the style of RTFM. But it is also an indicator. A good platform should be easy and fast to learn, just to show a proper and convenient solution. Here, I didn't feel it.

And I remembered a Soviet joke when assembling according to the drawings of the plane was the engine. And drawings was written — “the resulting product, modify a file”. I don't want to modify the engine to the plane with a file. I want everyone decently worked out of the box.

PS: I apologize in Advance if I hurt someone's “religious feelings.”

P. S. 2.: the Article was written about Zend Framework. And it does not attempt to challenge the merits of MVC, but unfortunately many are just so and took it.
Article based on information from habrahabr.ru

Популярные сообщения из этого блога

Approval of WSUS updates: import, export, copy

Kaspersky Security Center — the fight for automation

The Hilbert curve vs. Z-order