Answer the question
In order to leave comments, you need to log in
A few questions for smart developers regarding mvc and php?
Good day. Obviously, for someone the question is noob, but for me it is relevant.
We have our own development, an add-on for billing. Initially, there was a set of scripts, which gradually replaced and significantly supplemented the existing billing web-muzzle. So, now we isolate similar requests, break them into classes. I want to come to MVC, but reading the Gang of Four design patterns, articles on Habré and Wikipedia, I see that web developers distort the classic MVC model, hanging on the controller what it is not supposed to do. And most importantly, I go the same way, stepping on the same rake. Roughly speaking, now I'm busy breaking down the functionality of the system into controllers and models (a model has been created for almost every element of the database). Without touching on the views (smart is used, but we are not talking about templates), explain to the fool how to remove those jambs that I will list below.
To begin with, during the development process, I came to the following scheme:
index.php - entry point
class/controller/*Controller.php - Controllers
class/model/*Model.php - Models.
The rest is not important yet. The goals that were set when going to this scheme were simple: - From all the accumulated scripts, remove data output to templates;
- Remove all queries to the database into separate classes (including validity checks and messages to the change log);
- Get an array of data needed everywhere (tariff plans, settlements, etc.);
- Bring work with each data element to several variations:
index, create, update, remove plus / minus specific actions (like in yii);
- Authorization. Move away from the access rights of the old billing to the access rights in the new one.
All this is complicated by backward compatibility with the old web face, but it was decided.
So, what I was striving for, I’m coming up. But working in this direction, I already know that I need to rewrite it again. After all, creating a model, I imagined an array object with methods for updating data. And in the controller I assumed all the logic. Yes, it was a good idea to shorten the code. But again, this is bullshit.
Look, almost every model I have is a class that implements the ArrayAccess interface (to work with a class object as an array). But. Methods have been created to update the data. For example:
$user=new user(array('id'=>$uid)); # object creation
echo $user['name']; # Username
$user->update_name('Vasya'); # updating the name - checking, changing and logging
Already here I have questions. I have to pass an array when creating a user object. Why? Because in most cases we work with the user by id, but for example, the subscriber replenishes his balance by indicating the login (exactly as he enters his personal account). How can this line be optimized? And is it necessary?
Second. In the model class, I now have 2 more static methods (not in all models): search($params) and get_all().
search is used to search for elements among allowed ones. For example, employees of one branch cannot see the subscribers / tariffs of another. search searches with "visibility rights" in mind. At the same time, get_all is required when setting up the system. In the admin panel, those who are allowed access should see all data items. Look, in order to find subscribers who have a tariff plan with id=39 and the settlement Uvolovsk, you have to form the following request:
$params=array();
...
$tariff=get_param('tariff', 'int', 0);
if ($tariff>0) $params[]="tariff=$tariff";
$cities=get_param('city', 'int_array', array());
if (!empty($cities)) $params[]=”city IN (”.implode(', ', $cities).”)”;
...
$users=user::search($params);
Without getting into the details of what get_param is, I don't like that I'm already forming part of the request here. In fact, it seems to me that the controller should not be engaged in such an analysis. How do normal developers solve a similar problem?
At the same time, returning to the get_param function, it seems to me that it is also some kind of ugly rudiment, but I still have no idea how to get rid of it. get_param takes 3 parameters: the name of the variable in the request (get/post), the type, and the default value. Actually, it returns either the found value or the default value. Here is an example of a city controller:
...
function update(){
$core=core::getInstance();
$id=get_param('id', 'int', 0);
...
$city=new city(array('id'=>$id));
….
$do=get_param('do', 'string', '');
if ($do=='update'){
$name=get_param('name', 'string', $city['name']);
$phone_code=get_param('phone_code', 'int', $city['phone_code']);
$city->update_name($name);
$city->update_phone_code($phone_code);
header("Location: ?route=city/update&id={$city['id']}");
exit;
}
...
When there is a lot of data in the form, these get_param grows so much that you want to go to work as a handyman at a construction site. Again, how to get away from such ugliness?
I would be very, very, very grateful to those who will delve into the mistakes I have described and direct them in the right direction. It’s a shame to show the whole code, but maybe there is someone who can set aside some time for consultations in personal correspondence in terms of the mistakes that I make.
Answer the question
In order to leave comments, you need to log in
M- models ... to simplify - this is just a projection of your data store (be it ORM / ODM or regular files) onto objects. And that's all... They should not contain business logic. Also, the model should not know how exactly it will be stored in the storage, because then when changing the storage, adventures will begin. In this sense, I do not like the concept of AR in the form in which it is implemented in Yii. Some class CActiveRecord that knows a bunch of everything about what and how it will save, and from it we need to inherit our simple model that contains only a couple of fields. Again, there are different implementations of the ActiveRecord pattern, although I prefer DataMapper (for example, Doctrine), in which you have a separate component that knows what and where to shove. The logic for selections in this case is taken out in the repository, such record managers. The advantages of this method are that it is very easy to replace the type of storage in the system, expand and maintain due to the loose coupling of components. Controllers only know that there are repositories that have their own interface for each entry (ala getPendingPayments ()), and they do not know where these models come from, from the database, hang in memory or something else.
V - everything is simple here ... templates are designed to decouple the view from the logic of working with data, so that changing the first one would not cause editing of some service / controller.
C- it's more difficult. There are two concepts, thick and thin controllers. The latter are the most common, and include two layers: the controllers themselves and the service layer. Controllers decide what action to take with the data, depending on the parameters received, user rights, etc. They also resolve situations when there is no required data, various errors, etc. All business logic is taken out in a service layer. Services should be minimally aware of the context of use and only deal with what they were written for. Let's say the data validation service doesn't need to know anything about the payment processing service, or permissions, etc. He receives data at the input, and he checks them according to the rules described for this model.
There are also some recommendations regarding the use of thin controllers: duplication of controller code is normal. If you have several controllers that implement roughly the same thing. That is, if the differences are minimal, say, user rights are different, different views, different models (relevant for simple CRUD operations), then it’s better to have many very similar methods than to fence one base controller and inside fence the logic to process all this . Since all business logic is rendered into services, there is no duplication of it. And during the life of the project, a lot can change, and it is much more convenient to make changes to individual methods and not to one hefty super class.
Also, when working with services, the use of a dependency container helps a lot. Fortunately, there are a lot of ready-made implementations (both simple, like Pimple, and complex, like DiC in Symfony2 or Zend2, for example).
ps My opinion does not claim to be true. If someone saw something in this approach that is not correct, I will be glad if you point it out.
At the moment you are pouring from empty to empty (sorry).
I want to come to MVC ... web developers distort the classic MVC model - they distort it for a reason, in its classically pure form it is not suitable for the web. Instead, modifications are used. Which, as a rule, differ in the understanding that there is no model and there is no view.
Before refactoring, you need to set refactoring goals, for example (ease of expansion, performance ...), and only after that look at
patterns, etc. (but not vice versa). I got the impression that this is refactoring for the sake of refactoring, and the beautiful word MVC. I’ll call them beautiful a lot: MVVP, ORM, Observer…
What you describe has the right to life, but it is heterogeneous and diverse. Therefore, I would highly recommend that before writing, all the same, determine the goals, and supplement the question with them. For some reason it seems to me that you will find the answer to the questions when you write them.
Get an existing entry
$user=new user(array(‘id’=>$uid));
$user=user->create($data);
$user=user->find_by('id', $uid);
$user->update_name(‘Vasya’); # обновление имени - проверка, изменение и запись в лог
$user->update_email(‘[email protected]’); # так не пойдет
$user->update('email', ‘[email protected]’);
$data=array(
'name'=>get_param('name', 'string', ''),
…
'tariff'=>get_param('tariff', 'int', 0),
);
Regarding working with models, look towards www.phpactiverecord.org/ - AR pattern.
Forget about MVC, it just offers to divide the areas of responsibility between layers, the letters themselves for beauty and an example.
look, you have a tariff model and you pass parameters to it in the same place through an array.
you write $tariff = Tariff::findById(1); // SELECT * FROM tariff WHERE id =?
then you need to hide the old rates.
$tariff = Tariff::findById(1); // SELECT * FROM tariff WHERE id =? AND hide <> 0
Then your tariff is tied to the city, etc.
You do all these edits in one place in the Tariff::findById method, and you use pluses throughout the code.
The controller, in your version, you still do validation and work with form data through it, and then you get tired of it and create the validator and form classes and gradually, through refactoring, build up a dozen layers that will only do their task well. But having a legacy code, you don’t need to immediately think about how you will separate the classes, you need to refactor and splash on what happens.
I advise you to take Propel propelorm.org/ or Doctrine doctrine-project.org/
as ORM
Proven projects.
Hello.
This is how it is convenient to work with data:
Open the page /articles/, or /articles/123, or /articles/?date=2013-10-22
class ArticlesController extends BaseController {
# для страницы /articles/
function index ($get_params){
if ($get_params->date){
$articles = $this->Articles->find_by_date($get_params->date); // выбираем все статьи за заданную дату
} else {
$articles = $this->Articles->all(); // выбираем все статьи из таблицы
}
return compact('articles'); // этот массив пойдёт в шаблонизатор
}
# для /articles/123
function item($get_params){
if ($article = $this->Articles->find_first_by_id($get_params->id)){
return compact('article');
} else {
throw new Exception404;
}
}
}
$this->Article->create();
$article = $this->post('article); // массив
if ($id = $this->Articles->create($article)){
$article['id'] = $id;
}
$articles = $this->Articles->find_by_date('2013-04-15');
if ($authors_ids = array_map(function($article) {return $article['author_id'];}, $articles)){
$authors = $this->Authors->find_by_id(array_unique($authors_ids));
}
return compact('articles', 'authors');
Don't be afraid (learn?) to move methods from one class to another. Remember that PHP is already a framework and a paradigm to some extent. Assemble the code (structurally) in a way that is convenient for you as a developer, and then decide what you got M, V or C.
Just as an idea - take Phalcon and use it to create a model / controller for some of your entities and see how convenient / fast it is to work with this framework. I recently started studying it and so far I'm completely delighted. For me, the key point in choosing this particular platform for my next project was that this framework is a PHP extension written in C, in other words, it outperforms any other pure PHP solution in terms of speed / memory. Well, it has all the goodies of MVC , plus support for working with MySQL and PostgeSQL at the same time . Also, there's Zephir on the horizon, which will allow you to write compiled extensions for PHP. Maybe I'm wrong, but performance is important for billing, there are always a lot of users ...
PS. If there is a desire to test it under Windows, then write in a personal, there are nuances with the installation.
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question