⬆️ ⬇️

Zend Framework, subjective impressions

Recently, I was commissioned to develop some kind of web application. I will not go into details, but only say that the application is related to transportation planning. There is a public part, which can be used by any visitor to the site. There are internal interfaces for system operators. There are informers for posting on third-party sites. From a technical point of view, these are several dozen screens, many different forms, plates. Some screens use ajax, custom components written in javascript, and all sorts of beautiful drag-and-drop types. The data, as usual, is stored in a relational database in the form of a dozen tables. In general, this is not a completely primitive application, but I also cannot call it very difficult.



At work, I often have to design or personally code such applications. However, this project had one important requirement. The application must be developed on the basis of a serious and proven platform, namely on the Zend Framework. The use of hand-written "bikes" is unacceptable. Frankly, I still have no real experience with the Zend Framework. But the platform is well-known and stands behind a well-known developer. Many developers of the Zend Framework are generally considered as a standard for web development. So, all the more, there is a reason to learn something new and solid. Therefore, I enthusiastically took up this project.



Next comes a description of my personal emotions and impressions, so perhaps you should not take them too closely. This is exactly the kind of emotional remnant left after the project is completed. Zend Framework in a commercial project was used by me for the first time.



')

Zend Framework is positioned as an excellent, thoughtful and convenient platform for developing web applications. What is a typical web application in my personal opinion? Well, these are many different screens, many forms, many tablets. All this is actively working with the database, usually with a relational database. Well, I will be more specific, in 95% of cases it is from MySQL. So, from the platform for developing web applications, I at least expect good opportunities to create forms, different screens and convenient work with a relational database. And so I began to learn.



First of all, I needed to learn the basics of the Zend Framework itself. I have read a book dedicated to this platform “ Zend Framework. PHP web application development. The author: Vikram Vasvani . ” It seems that at the moment it is almost the only Russian-language printed book 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 in order to fill in the blanks and generally understand what else the framework can provide me. After all, the book considers not all the possibilities of the engine. Exhaustive and lucidly written official documentation is here framework.zend.com/manual/ru After about a week, I decided that we could start developing the application.



My worldview on coding



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



I respect OOP and the use of OOP patterns. But the application should be justified and the readability of the code or its further improvement should be simplified. Patterns for patterns - this is bad.



Good code is clear code. Clear code is a concise code. If you can write code shorter, then as a rule it will be clearer. The more logically related pieces of code fit on one monitor screen - the better.



If something does not suit me in the already existing solutions and approaches, I think about creating my own solution that will satisfy me more. Yes, many call it the “invention of the bicycle.” But I prefer to create a comfortable bike, instead of long and sadly finishing the file with someone else's, not very suitable solution.



MVC is great, but not in each case it simplifies and speeds up the development process. In some cases, it is possible to retreat from it, especially when it comes to the simplest actions, but there are many of these actions. (But I don’t want to say that MVC is not needed at all.) Here’s an example:



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') { // …. } //….   20-30   ... 




Yes, I have mixed here Model, Controller, Router and View. It looks like a piece of govnokod. However, in some conscious cases, I will write this way if it 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 an ardent mvc-fanaticism, and I am for common sense and deliberation of approaches.



In the draft in question in the article, of course, there is no such code. I do not think that I have enough experience to consciously retreat from the approach dictated by Zend Framwork, so I tried to fully follow his concept.



Now a little about the templates in web projects. I do not understand when using template languages ​​like Smarty. Well, why invent a new and generally limited language when there is a full-fledged PHP language? Is it really difficult for a typesetter to teach using PHP-shnye if (...) and foreach (...)? This is no more difficult than in Smarty, but we do not add a new exotic language to the project.

By the way, it happens that templates (or Views) are so complex that they have to use not only if and foreach, but also very complex constructs, including classes and objects that are inherited from each other. Although at the same time, the task of presentation remains the same - to generate HTML code from dry data.



In general, for templates I always prefer plain PHP, and the developer’s promise not to stuff any logic into the template. You can use the simplest type function to process a pattern.



 function renderTemplate($file, $vars) { foreach($vars as $_name => $_var) { $$_name = $_var; } require $file; } //  renderTemplate(“page.tpl.php”, array( 'menu' => array('/about'=>' ', '/contacts'=>'') 'title' => '' )); 




Plus of course the simplest strapping for shielding, for caching. It seems nothing more is needed for happiness.



Perhaps I have nothing against XSLT as a template language. But only for cases when there are not very many options for presenting information and they are relatively simple. Well, you will not make it easy on a bare XSLT template for any hellish form of structure and geometry which depends entirely on the incoming data. Both cycles and objects and polymorphism and inheritance are required here.



Installing Zend Framework



Freymork, as it turned out, weighs about 25MB and contains almost 3000 files. Not weak, I thought! Apparently everything has been realized there that one can dream of!



To begin with, I was a little upset by the Zend Framework application directory structure. It assumes a very large number of directories. When working on a project, I was constantly lost in this structure. Also, I found the rules of the name of the classes being created, the rules for naming files and the paths to their placement, to be a bit extraordinary. The logic is there, but to be honest, it was possible to make all this more intuitive. And it is especially tiring traveling through the directories of the presentation scripts. Although, apparently, if you work with this framework for a long time, you will get used to it.



But I was very tired of the ritual of creating new sections of the application: Create directories, name the file, name the class, and name the controller. First, do it with the controller, then with the view. Yes, there is a console utility, which in some cases allows you to do this automatically. But in some cases it is incapable.



It turned out that the Zend Framework is not designed to work with shared hosting, because to start it requires to register in VirtualHost a directory different from the root of your project. So you should have at least a VDS server. Well, yes, I heard the opinion that if you develop something that works on 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 revolve on shared hosting. Well, anyway, after a few swings of the file, I still made the project go up on shard hoting.

Work with databases



First, I needed to create data models (within the framework of the MVC concept). As it turned out, Zend Framework does not provide anything ready for creating models. In fact, you just need to write your own class, whose methods will perform a particular operation to work with the database. Those. In some cases, the model class is just a wrapper over an SQL query, and sometimes SQL queries with some additional PHP binding.



Zend_Db components are used to work with the database in the framework. It allows you to work with most common DBMS through SQL.



It also allows you to make queries without using SQL, but by constructing a kind of cunning object that inside the framework turns into SQL. It just seems to me that to write a more or less complex query, it is still easier and more understandable to write a SQL query than to construct this wrapper, which, as it turns out, allows you to create far from any complex query. In general, I believe that it is better to study SQL itself rather than trying to force it to form a framework, and hope that it will do it in an optimal way. Native SQL is much better readable, understandable to all programmers and allows you to use all the capabilities of a particular DBMS. You will say that using the object constructor of queries allows you to abstract from the dialect of a specific database? With complex queries - it is not always possible to abstract, even with a cool designer. Well, one more argument - I don’t remember a single project where I would need to change the DBMS, and it didn’t imply a global rework of the entire project, the business of logic and the data model, i.e. writing a completely new project based on the previous one. Saving a piece of the data model class from the previous version against the general background will not make life much easier.



So, what does working with SQL look like in Zend Framework? Well, something like this:

$ result = $ db-> fetchAssoc ('SELECT * FROM items WHERE age> 18');



And then you can work with $ result as with an associative array in the form of a table (although this is not exactly an array), which is very convenient. You can pass an array for further processing, and you can also directly transfer it to a view for display on the screen.



There are some more convenient methods that retrieve the results of SQL queries:

fetchCol () - retrieves one column. Convenient when you need to get a list of ID-NIS

fetchRow () - fetches the first row. It is convenient when you need to select one entry by the primary key.

fetchOne () - Retrieves only one value. It is also convenient when you really need to get one value and do not want to write code to pick out one of its elements from the array.

fetchPairs () - retrieves pairs of values ​​as an associative array. Creates a key-value hash. Very useful to create different dictionaries.



All these methods allow to reduce the number of lines of code in trivia. Remove any additional cycles, extract elements, etc. As a result, the total amount of code in the models decreases markedly.



The fetchPairs () method is good. And now, how to get a little more interesting and necessary dictionary, where the primary key of the database is in the key, and the database is the record-value as the key? A very common task. The answer - and no way! Do it with your hands, call fetchAssoc (), then spin the loop and generate a new dictionary array. It's a shame!



Okay, go ahead. It is necessary to extract data from the table and build a tree-like array from them. It turns out this common operation is also not implemented. Those. There is nothing like:



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



Go ahead. Parameters must be passed to SQL. Well, yes, at first glance, everything is simple. This is done like this:



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



You can even put a few pleyskhodelerov.



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



Only here when the program becomes complicated, the complexity of requests grows, and there may already be dozens of parameters. Looking for named placeholders. Considering placeholders according to their ordinal number is far from clear code. As it turned out, Zend Framework does not implement them! They work, but only if the selected database supports them. For example, if you selected MySQLi, then you cannot work with named placexers. Was it really so hard to do the emulation of the named parameters ?! Well, this thing turned out to be solvable. It turns out that if you select Pdo_Mysql instead of Mysqli as an adapter, then the named parameters will start working, because they are emulated inside the PDO itself. Frankly, I came to this not immediately. The offense subsided, but the unpleasant aftertaste remained.



Often in SQL queries you have to use a code like this



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



where the list of id-shek is dynamically generated from an array.

I personally have not found a way to do this on the Zend Framework beautifully. There is a certain quoteInto () method. You can use it



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



We are resigned to the inability to use named parameters in the query, with the creation of a complex query of several pieces, but we get the necessary SQL code. Only we will still get a syntax error if the array is empty, because the SQL will turn out to be SELECT * FROM item WHERE id = IN ().

So either we wrap everything in if (count ($ idList)> 0), or we add an obviously invalid identifier to the array each time, for example, -1. Krivenko, but the decision. But still, it turns out? that a complex SQL query can be composed only by concatenating strings. And I prefer to see any SQL query as a single piece of code, without any PHP interspersing. And damn, mixing HTML and PHP is considered bad, but SQL and PHP, you see, is normal.



In general, Zend_Db was not a big deal for me. I did not notice any great benefit or strong comfort from its use. By the way, Zend_Db weighs more than 500KB.



In general, to work with the database, I prefer to use a homemade small class with the following public methods:



connect ($ config) - Connects to the database

fetchTable ($ sql, $ params) - Returns an array of records.

fetchRow ($ sql, $ params) - Returns one record

fetchCol ($ sql, $ params) - Returns one column.

fetchOne ($ sql, $ params) - Returns the scalar value.

fetchPairs ($ sql, $ key, $ value, $ params) - Returns an array key - value

fetchDict ($ sql, $ key, $ params) - Returns an array key - record

fetchTree ($ sql, $ key, $ parent, $ params) - Returns a tree

exec ($ sql, $ params) - Executes a non select query

getInsertId () - Returns the last generated auto-increment

getAffectedRows () - Returns the number of records changed.



Execution results are common associative arrays of a logical for each case structure. Parameters are always named, you can pass numbers, strings, and lists. The class is thrown by exceptions if a syntax error is made in SQL. This “bike” weighs around 10KB, and to be honest, I always have enough of it. Other programmers need 10 minutes to learn it, and for studying Zend_Db - hours of careful study of manuals, and still there are questions. By extending the class and overriding several virtual methods, you can add support for any other database.



Forms



The next most important thing for a web application is forms. Usually a lot of them. The structure and appearance is also very diverse. Zend_Form is used to work with forms in the Zend Framework.



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



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




Having created all the fields - add them to the form object



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




Only now when there are a lot of fields, we will surely forget some of them to add to the form. Well, I don’t like to be horrified when something needs to be enumerated in the code, and then again somewhere else in the other place to re-list it all. And here it is necessary to specify each time the name of the variable of the field object and its name for output

If addElement () did not return a link to the form, but a link to the added element, then with the help of the fluent interface the code would come out much more concisely and without any repetition. Something like that



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




But so it is impossible!



As you noticed, here you can use ready field validators. Not bad. Let's try to make a field for entering e-mail and enter the curve email address. To do this, use the standard validator EmailAddress.



Let's try to specify the curve email “xxx”. We get a message about incorrectness:



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



Fine. Of course, it would be necessary to translate the message into Russian, but there it can be easily done. And now let's try even more crooked address “I@_domain.xx”.

And this is what we get:



'_domain.xx' is no valid hostname for email address 'i@_domain.xx'

'_domain.xx' DNS hostname but cant match

'_domain.xx' 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 'i@_domain.xx'



6 posts! Who needs it? Can an ordinary user understand what a TLD is, quoted-string or dot-atom? The average user only needs one single message - “Incorrect Email” and that's it! But standard tools do not allow this. To get rid of the output of this heap of messages - it is proposed to write your own validator class.



Well, let's say we made a form, set up all the fields, all validators. Now we derive it. By default, the HTML code form will look something like this:



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




And in DD elements I will wrap up even hidden fields! And then we begin to wonder what kind of strange voids formed in the form. In general, the framework has no intelligence for special processing of hidden fields.



What if the simplest DL list doesn't suit me? Suddenly I need to make a form with a complex structure? Here it is proposed to use classes decorators. Decorators are classes that allow you to wrap form elements in certain HTML tags, for example, you can wrap an element not in a DT tag, but for example in a DIV. Oh God! The “correct” MVC framework forces us to do layout directly inside the controller, and in the most unreadable way!



In my opinion, the layout of non-standard forms should be dealt with exclusively by the layout designer. He should just make a presentation script that contains all the layout of the form. And to use a dozen classes of decorators inside the controller is, to put it mildly, crooked.



Let me give you an example of the simplest “bicycle” for working with forms, which allows you to concisely and conveniently create forms.



Configuring the form:



 $form = new UniversalForm(array( 'name' => array('label'=>'', 'required'=>true), 'email' => array('label'=>'', 'type'=>'email'), 'birthday' => array('label'=>' ', 'type'=>'date'), 'sex' => array('label'=>'', 'type'=>'choice', 'items'=>array('m'=>'', 'f'=>'')), 'code' => array('label'=>'', 'regExp'=>'/^\d{6}$/', 'regExpMessage'=>' ', 'value'=>'000000'), 'action' => array('type'=>'hidden', 'value'=>'updateInfo'), )); 




Here, to reduce the amount of code we make it possible instead of explicitly creating objects of form elements, we simply create a configuring array. Although this does not exclude the possibility of creating field objects and validators manually. Well, then, use the form:



$ form-> setFromPost (); // Set data from POST

$ form-> isValid (); // Check that the data is valid. If not, error messages will be set, which the form submission script will display.

$ data = $ form-> getValues ​​(); // Get the values

$ form-> setValues ​​($ data); // Set field values. Although you can do the same through the configurable array in the constructor

$ form-> render ('form.tpl.php'); // Generate HTML. If no template is specified, the default template will be used.

$ form-> renderJs (); // Generate JavaScript validators that duplicate client-side PHP validators.



Well, now a few methods that are wrong from the point of view of MVC.

$ form-> getSqlSet (); // Return a piece of SQL code

Can use so



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



The following methods are used for the same purpose.

$ form-> getSqlFieldsList ();

$ form-> getSqlValuesList ();

Yes, this is not entirely canonically correct decision. But then the set of fields should be described only once in the form constructor. In case of its changes, all SQL queries will automatically change.



Adminy



As a rule, any complex web application requires the creation of some admins or control panels. For example, to set parameters, or to edit directories, etc. Those. at a minimum, some tool is needed to create record list editors. At least flat lists, and for complex projects, these lists must often be hierarchical.



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



Conclusions and impressions



As you understood, this “correct” framework made not a very good impression on me.



But perhaps Zend Framwork can still be mastered in the following cases:

1. If you do not have enough experience to make a tolerable frame for your application.

2. If you do not know how to work with MVC at all, but want to experience this ideology

3. If you do not have a good idea about OOP and the use of typical design patterns, then Zend Framwork will show you many examples. There they used OOP wherever necessary and not necessary.

4. Also Zend Framwork will be fine if you make an uncomplicated application, whose work more boils down to extracting data from the database and displaying it on the screen. For example, some sort of news portals or product catalogs.



Although it may still be better to turn your eyes to some other framework? Zend Framework has already begun to lose ground, and perhaps for good reason.



And personally, I came across inconveniences in almost all components of the framework with which I tried to work.I regularly grumbled phrases like “It was possible to make it more convenient!” Or “Well, why didn't they do it!”. I got the impression that the main task of the developers of Zend Framwork was to make all the OOs. All that is possible, everything is wrapped in wrappers, adapters, and it does not matter whether it will be convenient or not. The level of abstraction in the library is extremely high, so high that it is hard to understand. And most importantly, almost any component must be finished to fit your needs. In raw form, everything does not work as you want.



Yes, I am well aware. Surely I misunderstood something in the framework. Surely missed something in manuals. And now I will receive comments in the style of RTFM. But this is also an indicator. A good platform should be easily and quickly mastered, immediately show the correct and convenient solutions. Here, I did not feel it.



And I also remembered the Soviet anecdote, when collecting an airplane according to the drawings, a steam locomotive was produced. And in the drawings was written - “the resulting product - modify the file”. I do not want to modify the locomotive to the plane with a file. I want everything to work tolerably right out of the box.



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



PS2 .:The article was written about the Zend Framework. And this is not an attempt to challenge the merits of MVC, but unfortunately many have perceived it that way.

Source: https://habr.com/ru/post/150762/



All Articles