How Do You Convert a Page-Based PHP Application to

2019-03-09 13:27发布

问题:

I've been struggling for some time now with exactly how to recode a page-based PHP application using an MVC framework. Just for background, I am having to move the app into MVC because my boss is making me. Anyway, I've sat down, and printed out the directory structure. I've then started trying to plan how I can convert these pages into controller/action pairs. Some things seem very straight forward. For example, I had a couple pages devoted to add/edit/delete of a user. That's very easy to create a "user" controller, and add methods or actions for add/edit/delete. Where I am having problems is deciding when to actually create a controller versus making something just an action as it's not always so clear cut. For example, a login controller versus user/login, or a register controller versus user/register. To me, if the object can do something it makes perfect sense to be an action, but it's just not always so clear cut.

Another example would be, that I have about 12 form pages that are used to create a "plan". In my head I would think I needed to create a "plan" controller, and each one of the old for pages would then become an action. So I'd have one controller with 12 actions (methods). The problem for me is, that although all 12 of these pages are data entry forms that eventually make up this "plan" that's all they have in common. Each of the pages use different tables in the database, and have nothing else in common with each other. Basically by creating a "plan" controller I'm just really using that as a grouping mechanism; not necessarily using it because they have something related with each other. At least in the "user" controller example above; each one of those actions uses the same "user" table, so it makes sense to group those actions into one controller. Should I make each one of those data entry forms there own controller?

I guess it just boils down to letting myself use controllers as a hierarchy structure entity instead of objects/actions. It just seems it's really easy to fall into that trap using controllers the wrong way. Does anyone understand what I'm saying? Hopefully it's not too confusing.

EDIT: If I try and stick with one controller per view; I will then be keeping code per request to a minimum. Is this the best way?

EDIT: From what everyone is saying, it seems that the one controller per view would not be in my best interest. I still have some concerns because it seems that a controller could become fat in a hurry, but that's for another discussion. I also still have some issues of when to make that decision to use a controller instead of an action. A good example would be stackoverflow itself. At the top of the page, you have a "Questions" selection which we could assume takes you to the "questions" controller. I say this because on the right hand side you can choose to "Ask a question", which the URL points to "questions/ask". That makes sense that your using the ask method of the questions controller. What confuses me is then you have the "Unanswered" option on the menu. It looks like this has a controller to itself. Why wouldn't it just be an action under the questions controller as in "questions/unanswered"? That's where things become muddy for me.

回答1:

since your boss is buzzword-happy, tell him to look up "refactor"



回答2:

Slightly off topic to your question... for the love of god don't forget your old URLS. The reason being is that the minute you flip the switch on the new site and its pretty URLS, all of the aggregated content held by the search engines will slowly expire, taking your SEO stats way down.

UPDATE: If I try and stick with one controller per view...I will then be keeping code per request to a minimum, but is this the best way?

If I understand you correctly, each controller would produce one page. This can be a really bad idea that I've experienced first hand in a maintenance position. If you have content that doesn't really match well to the OO paradigms then put it into a category: press releases, one offs, informational, etc and stick those into a controller that serves just those views or better yet a number of controllers per category to leave more fine grained control.

The "MVC" framework I dealt with that did the one controller to view quickly became a convoluted mess with lots of hacks and spaghetti code.

I have about 12 form pages that are used to create a "plan".

I've had to do something similar and as goofy as it seems, having a "planningform" controller is probably best. Yes there are multiple tables to feed, but you can do simple stuff like $_SESSION['plannerController']['subject|action'][key][value] to keep each part of the form overall in check. Having each method handle part of the multi-page form can also be a benefit ( ex. What if your boss says they only need 6 out of the 12 pages or what if part 7 becomes really complicated and needs stuff like autocompletion ajax ).

grouping mechanism not necessarily using it because they have something related with each other.

I generally like the one class, one purpose paradigm but sometimes it just doesn't make sense to try and do that. There are cases where its clear cut, right out of the text book OO and other times the academic BS can go to hell. Your going to find stuff that doesn't fit the OO mold, so you can take a step back and try to merge things into common categories.



回答3:

What is the point of having one view per controller?

Again:

  1. You are not loading more code than is necessary per request. Would you load a library, package, dll, that you didn't require? Of course not. So don't create a massive controller file where only a small portion of the code will be executed per request. Also, smaller files are easier to maintain imo, just like smaller methods and modular code.
  2. there is a clear 1:1 link between controller and view (especially if they share the same name). This is a convention. It's clear and consistent. If I am looking at a view template, I know exactly the controller that loads it based only on the file name. There is no thinking involved. No decisions. No compromises.

What need is there for a separation of concerns when everything is just pair matched?

What need is there for a separation of concerns? If you want to group related pages (controllers/views), lump them in directories.

I'd be more inclined to write a controller that controls several views that are related in some way. For instance, the aforementioned add/view/edit of a user. You'd want to keep similar functionality together rather than searching through many files for the code you want. It's also handy to have all the methods defined (for a particular object) in one place. Makes maintenance MUCH easier.

I'm going to respectfully disagree. If I have a single view that is responsible for adding/viewing/editing a user, then with the 1:1 convention I know exactly the controller responsible. On the other hand, using your suggestion of grouping similar functionality, if I have a manager controller and a user controller, which one contains the add/view/edit for a manager? User or manager? Now you have to think, or search.

I worked on a project using a PHP framework that created a separate file per 'action'. Named like 'object(action)' and it became a NIGHTMARE to maintain.

I'm not suggesting that.

I've been using Django for a little while now which keeps all the models in one file, all the views (controllers) in one file, and the templates (views) separately. [Django isn't MVC but for these purposes let's pretend it is]. This allows you to group together common code in one place and maintenance becomes much easier.

I'm feeling whoosy now. I don't know Django, and I'm assuming the single files are optional, but there is no way I'd maintain a file with tens of thousands of lines.

My only advice is - don't try to organise your project based on some ideal of MVC. Organise your project how it makes sense to you and your domain.

No, no, no. That is very dangerous advice. Design patterns, coding conventions, and frameworks were designed for a purpose - best practices and consistency. Only a guru should step outside conventions and only if he/she works alone. Even within the confines of a framework, I constantly strive for greater consistency so that I don't have to think when writing or maintaining the code.



回答4:

In making a move like this, you're not just changing the implementation of a particular site, but rather changing the way in which it's perceived. You're going to have to do a hefty bit of recoding anyway, but you have the advantage of understanding the concept and having experience in how to do it.

My suggestion would be to sit down and pretend you know nothing about how to implement the site, but everything about what issues are associated with the kind of site you're working on--its requirements and its gotchas. Create an MVC-based conceptualization of how to do the site, and then try to reuse as much code that you have as possible. Code reuse may not ultimately be feasible, but on the bright side, at least you've solved many of the same problems before, so it shouldn't take as long to implement.

Don't forget that you can always use routing to maintain a sensible URL structure for something like a series of form pages using different controllers, allowing you to use different controllers, actions, and views while still being able to borrow from the same layout or template.



回答5:

What is the point of having one view per controller? What need is there for a separation of concerns when everything is just pair matched?

I'd be more inclined to write a controller that controls several views that are related in some way. For instance, the aforementioned add/view/edit of a user. You'd want to keep similar functionality together rather than searching through many files for the code you want. It's also handy to have all the methods defined (for a particular object) in one place. Makes maintenance MUCH easier.

I worked on a project using a PHP framework that created a separate file per 'action'. Named like 'object(action)' and it became a NIGHTMARE to maintain.

I've been using Django for a little while now which keeps all the models in one file, all the views (controllers) in one file, and the templates (views) separately. [Django isn't MVC but for these purposes let's pretend it is]. This allows you to group together common code in one place and maintenance becomes much easier.

My only advice is - don't try to organise your project based on some ideal of MVC. Organise your project how it makes sense to you and your domain. MVC was proposed to limit complexity, not increase it.

For your plan example, I would make that into a controller. www.example.com/plan/1 .. /plan/2 etc. It makes sense because the actions are logically grouped together to form some task.

A controller, in my opinion, should be used for managing a task or an object. It's methods should be the specific actions required to complete that task or modify/use an object.



回答6:

I went through something like this last year. I converted my mostly static PHP web site pages to use the Kohana PHP Framework. I made each web site section a controller, with views for the individual pages. The main page views included other views for header, and footer. Some of the views, such as for articles, were reusable by different controllers. The result was an MVC site with the same page URLs as the original web site.

EDIT: The URLs are in the format of /controller/method?arguments. As an example, on my site, the URL for /computer/article.php?id=# uses the article function in the computer controller. The computer controller, in turn, uses an article model to load the data into an article view with nested paragraph views. This also illustrates that even though the prior version of the site had page names with the .php extension in the URL, this can still translate to a controller class method and the same URL works in the MVC based version of the site. This should give you and idea of how Kohana might work for your site.



回答7:

I also still have some issues of when to make that decision to use a controller instead of an action. A good example would be this stackoverflow website. At the top of the page, you have a "Questions" selection which we could assume takes you to the "questions" controller. I say this, because on the right hand side you can choose to "Ask a question" which the URL points to "questions/ask". So that makes sense that your using the ask method of the questions controller.

Well, we don't know if "ask" is an action of the "questions" controller. This site could be routing the "questions/ask" url to a different controller, or "ask" could be a controller in a "questions" directory. Also, if you bring up the "questions/ask" page, you'll notice the form gets posted to "questions/ask/post". Now "post" could be a parameter for the "ask" action method, but I'd guess it is a "post" action method of an "ask" controller.

Who knows. But please consider this: The "Questions" page would require several times more code than the "Ask Question" page. Would it make sense to load the "Questions" page code when loading the "Ask Question" page. I'd think the stackoverflow guys would be smarter than that.

But what confuses me, is then you have the "Unanswered" option on the menu. It looks like this has a controller to itself. Why wouldn't it just be an action under the questions controller? As in "questions/unanswered"? That's where things become muddy for me.

Another reason why I think the "Ask Question" page has its own controller. The "Questions" page has much more in common with the "Unanswered" page.



回答8:

If I understand you correctly, each controller would produce one page. This can be a really bad idea that I've experienced first hand in a maintenance position.

The "MVC" framework I dealt with that did the one controller to view quickly became a convoluted mess with lots of hacks and spaghetti code.

Was this because of the framework, the developer, or both? How was it difficult to maintain? What hacks were involved and why?

The 1 controller to 1 view works well for me in the php MVC frameworks Zend, CodeIgniter, and Kohana. In ASP.NET, although not MVC, 1 webform/view is mapped to 1 code behind file.



回答9:

The whole concept was made more clear to me by a series of three blog posts by Nemetral that explain the pattern as it evolves from a traditional Web page script and thus as the PHP logic becomes separate from the presentation logic, thus in terms of MVC, how a model becomes separate from the view . According to Nemetral:

  1. The first step is to move all PHP code that was up to now coupled with HTML tags, to the head of the page.
  2. The second step is to move all HTML tags to a separate file and access it via a PHP include. So when a request is made it is directed to the PHP code (controller and model) and this code then requests the HTML tags or presentation (view).

I write more about this in my dissertation: http://kreus-cms.com/kreus/pages/written_work



回答10:

Assuming you have some sort of tests at hand, covering most (all?) of the functionality of your application.

  1. Flesh out a really basic structure of how your C-part will look like.
  2. Implement this structure ignoring the M and V, more or less just copying snippets of code from your page oriented files into your controllers
  3. Test that everything is still working as expected
  4. Go through every controller, extract an "M" and a "V". The "M" might be just an Active Record (I know, that's not a real model) or a row table gateway or something which is pretty fast to implement. The controller should now become thinner and thinner
  5. Test that everything is still working as expected
  6. Now that you know everything about your application, refactor again to extract a domain model if applicable. In some cases, this might just be a waste of time (CRUD-only/-intensive applications)
  7. Test that everything is still working as expected.


回答11:

Where I am having the most problem is deciding when to actually create a controller vs making something just an action because it's not always so clear cut.

Wherever possible, use one controller per view. That way:

  1. you are not loading more code than is necessary per request
  2. there is a clear 1:1 link between controller and view (especially if they share the same name).