This is more or less a framework-centric version of a past Stack Overflow question, which is about how most introductory material on MVC applications tends to present a tight coupling between models, views, and controllers. For example, you'll have a User table that is modified by a User controller which in turn pushes filtered data to a User view. It's my impression that a lot of MVC frameworks tend to reflect this pattern as well. This is all fine and well for what it is, but it never really leads me to anything beyond building and displaying monotonous lists of things with an HTML form.
The MVC framework that looking at right now is Lithium, which seems quite interesting as a case study of clever PHP5.3 coding techniques. On one end, Lithium has a Model class that offers wrapper objects around a individual tables, and abstracts away some simple queries. On the other end, it's got a nifty convention of routing URLs to method calls on controller objects, which then render to display templates.
But in the midst of this, I find myself at a loss as to where to place all of the interesting logic that relates data in table A to data in tables B through Z. Or at least, I'm not sure where to place such logic in a manner that's consistent with the design of the framework. To my understanding, Lithium's Model abstraction doesn't do much more than eliminate some row-level insert/update/delete boilerplate, and the controller/view architecture seems mostly about user interface. I wouldn't want to put a lot of business logic in the same Controller class that is receiving routed function calls from URL requests.
My instinct would be to fill the gap with a bunch of my own code that exists more or less entirely outside of the framework. I'm not sure if I ought to expect more than that, but given how rigidly structured everything else is in Lithium, it feels somehow unsatisfying, like I could have just rolled my own boilerplate-reduction code without the overhead of grokking the source of a big framework.
What am I missing here? Is there a recommended architecture or philosophy to using this type of framework?
One thing you have to remember with Lithium is that theres no production ready release yet (although some sites are using it in production).
The main missing feature right now is model relations. With relations in place i assume your question would be partially answered as that is an important brick in the big picture of creating more complex applications.
You could check out the x-data branch where the work on relations should be ongoing.
For the second part of writing domain logic the simple answer is "in the model".
See this (rather useless) example of extending model functionality for example.
Another example to look at is the open source mini application Analogue that is one of the few working open examples of Lithium in use. The Analogue model class shows a slightly more meaty model.
And finally its a matter of connecting the dots between M, V and C.
A Lithium controller should mainly delegate jobs over to models, and perhaps restructure the input data if needed.
The simple examples of having a Post model, PostsController and views/posts/add,index,etc doesn't mean that you have to merely have Post::all() in there.
PostsController::view need to load a set of Comment models probably.
So you will toss a lot of your own code in there, of course! Else it would not be much of an application. But keep the domain logic tied to the model where it is natural.
Need to verify that blog post has a
unique title? Write a validator.
Need to ensure the user has write access to the post? Override the save method and verify it, or filter it.
But I think we need to wait for relations to land and a 1.0 release before we can fully judge how structuring applications in Lithium can be solved best.
Related
Earlier I asked this question:
should-i-create-an-object-or-work-with-an-array
I am now trying tho think beyond the concept that I was working with. Please share your thoughts with me. I want to GET this.
If I would setup an MVC in combination with a data mapper, would this be logical, for a forum for instance:
All important things are entities. Post, Thread, User, Forum.
Basically I see a controller as a page. It may choose to show different templates (e.g. list, or form), but it IS the page, more or less.
Via the router I would have the needed controller loaded, to get the data and have it displayed in my template.
Now how would this work, for displaying all posts in a thread:
route is set to thread-> load thread controller -> controller asks entities (post, user) for the info -> entities tell the mapper what they need -> mapper gets it from database and returns it to entities -> entities return info to controller -> controller returns info to view -> view displays.
Is that the right idea?
Now where did the "model" go then, from the MVC? Or am I missing steps?
I do not want to use third party tools, I want to build it from scratch, to understand everything that goes on.
How do I start this off right?
I tend to see the page as the sum of the controller (the logic required prior to rendering) and the view (which determines what the page looks like). In general, I would recommend that you don't write your own first, since you'll be working to a design pattern that you've not yet fully understood. I think it would be better to pick a popular PHP framework and see how it is implemented, and then if you are still minded to, try writing your own. Coding it all of this is a lot of work, in my view, especially if you want to write an ORM too!
Although we shy away from framework recommendations on Stack Overflow, I understand from comments here that Symfony2 is thought to be one of the best implementations of this design pattern, in particular because of its use of dependency injection. Do read some of the questions here with the 'mvc' tag too - there's plenty that can be picked up. For what it's worth, I wouldn't get hung up too much on design patterns (i.e. whether framework X implements pattern Y) - so long as your apps are modular and easily testable, that's a big win.
I have almost completed a PHP project, using MVC, jQuery and Ajax. It is pure PHP project. I don't use any frameworks in the code right know. I would like to change that.
Doing some research, I found, that Yii turns out to be one of the best frameworks out there.
Is it possible to somehow migrate pure PHP project to Yii?
If so, then how to do this? Which steps should I follow in order to reduce the workload and enjoy the benefits the Yii framework presents?
I'm a total Yii newbie, any insights appreciated.
TL;DR : Don't do it. It's a really horrible idea.
The Rant ..
"Framework" is not a magic sauce, that you add to a project, to make it better and shinier.
Doing some research i found Yii turns out to be one of the best frameworks out there.
What a strange research you have done .. I would love to see the materials. Especially, since I would rank it as 3rd worst PHP framework. Only surpassed in it's awfulness by CodeIgniter and CakePHP.
And the reason for it is the extremely bad quality of code, that this framework displays, combined with the bad practices, that it perpetuates.
Why avoid migration?
From your description is obvious, that you are NOT familiar with this framework and have no previous experience with it.
In management of projects there a subject: risk management. And in this case, adding a previously unused framework in final stages of project would constitute a high probability an high impact risk which also, due to the sage of project, is completely unmitigated.
It means that there is a really good chance that something will go wrong. And when it does, it most likely will sink the project. Or at least push back the release data by significant length of time.
In a perfect world frameworks are used to simplify the repetitive tasks of development, at the cost of some performance. Those are the tasks you do at the start of the project. You are not at the start of a project. This means that you will gain no benefits from this "maneuver".
Why not Yii?
As I noted before, there are also reasons not only for avoiding adding framework to an existing project, but also reasons why to avoid Yii in particular.
The inheritance nightmare
All your controller will extend class CController, which extends CBaseController, which extends CComponent
All your "models" will extend ether CActiveRecord or CFormModel, which extends CModel, which extends CComponent.
Both of there chains contain static variables and execute static methods on multitude of different other classes. Combination of these factors will make debugging extremely difficult and tedious.
Global state
There are several forms of global state. One that people in PHP usually know are global variables. But that is not the only form. Every time you have a class that contains a static variable, it also creates a global state, that can (and almost always - will) cause seemingly unrelated instance mysteriously interact.
Use of global state is a core mechanic. You will see static calls all over the codebase, and the Yii's configuration file would not function without global state.
And every time you call Yii::app() you are accessing and/or changing it.
This makes unittesting impossible for Yii applications. And debugging turns into exercise of using grep on your whole project.
Tight coupling
When you create an application in Yii. It becomes bound to it. You cannot execute parts of your application without launching the full framework. Mostly it is due to the static call, that you end up adding to your code.
Every time you add a static call in your own code, that piece of code becomes tied to the name of the class. That essentially is tight coupling.
As you might have noticed (hopefully), there is another way how to achieve the same effect - the use of new operator. That is another way of coupling some code of yours to a specific name of a class.
No interfaces .. none .. whatsoever
No matter how horrible the configuration of a Yii project is, the configuration file was a well intended gesture. The least harmful way to introduce external code and replace existing components in so messed up codebase.
But unfortunately it brings in the focus the problems caused by lack of interfaces and the existing coupling.
One of the component that developers will try to replace is the CUrlManager. Mostly due to way how you can pass additional parameters.
An interface in OOP specifies the contract between two instances. It lets you define the capabilities of an instance, the methods that can be used by others. When it's not there, in a large codebase, you are left guessing, which methods are required and which are not.
In case of Yii components the problem is compounded even further due to static call and deep inheritance. The above mentioned CUrlManager extends CApplicationComponent, which extends CComponent. Also the same file defines CUrlRule and CBaseUrlRule classes.
When you are writing a replacement, you have to write some code, plug it in the configuration and then test it by running your applications. That way you know which method (or parameter) next you need to add.
Basically, it's the "save-an-see-what-blows-up" method of development.
That's not MVC!
Yii does not implement MVC or any of MVC-inspired design patterns. What it calls "MVC" could be described as ActiveRecord-Template-Logic pattern.
Instead of having proper model layer (yes, it should be a layer), the creator(s) of Yii opted for collection of active record and form wrappers. This forces the application logic to be forced in the "controllers".
On the other hand you have glorified templates, instead of proper view instances for containing presentation logic. It is somewhat mitigated by use of widgets, but those instead suffer from SRP violations, because widgets are forced to contain bits of presentation logic and perform partial rendering. The rest of presentation logic ends up again in the controllers.
And to make it all worse, the "controllers" also have to deal with authorization. This usually will mean, that whenever you change the access scheme, you will have to go through every single instance of CController to check whether it needs to be changed too.
It's not MVC. It's a mess with names taken from MVC design pattern and slapped on some components.
All the small things ..
The framework also comes with few minor issue, that do not deserve a separate section:
Defining more then one class per file:
This will get annoying quite fast, because there will be classes that are shoehorned at the class files with completely unrelated filenames. This will mean, that debugging will quite often require use of search.
Slapped on "modules":
By the looks of it, the modules where added to the framework after the fact. Which is why, when you need to set default module, you will have to set it in the configuration files parameter, that is called 'defaultController'.
I actually recently converted a MVC pattern website I had built from the ground up into Yii. It did take some time to set it all up but in all honesty it was well worth it. I was able to throw away a lot of code because there were already Yii extensions doing what I needed. I would also suggest that you keep your database because you can create the controllers and Models using Gii which will save you a ton of time.
I don't know of any quick solutions to this. It depends upon how the code was written. You have the database and your views so it is not really a complete new project when you take into yii. Yii will generate the database models for you. You already have the views from the existing project. Write the controller and actions and modify the views if necessary.
try these links as they refer to the same problem.
How do you convert an old oop project into Yii
tips on migrating an existing site to Yii
Drupal to Yii Migration
Since you already have a code in mvc, things will be much easier for you to migrate. However, while migrating to Yii, since it can generate controller and model very easily using gii, you can take the advantage of it.
So, first generate controller and model using gii, then you can replace your existing code (by replace I mean, substitute your code to the specific function in the controller and model) to the built in controller and model so that the functionality of your site still works. You can modify your view accordingly. But that won't be much of a work.
You can simply register your script for ajax, jquery and css. Those will work as well.
And yes, Yii is the best framework out there so take as much benefit as you can.
Thanks,
Ujjwal
In this project you converted php to yii framework. Its really easy for you if you do following step.
Since you already have a code in mvc, things will be much easier for you to migrate. However, while migrating to Yii, since it can generate controller and model very easily using gii, you can take the advantage of it.
second, If your database is accurate then 50% work complete.when you create CRUD operation using gii then automatically model-view-controller create.if you create mvc in php then it benifit for you.
third,You can simply include your script for ajax, jquery and css. Those will work as well you create a folder in themes(CSS,JS,AZAX,BOOTSTRAP).
four-Protected->view->layout, where you can change your theme..thats all
you also help www.yiiframework.com/doc-2.0/guide-intro-yii.html
if you think my answer is help you then rating me...thank you.
I work in a web shop as a PHP programmer. Most of the time we use good coding practices but not much structure to the site as a whole.
I have now entered my phase of being bored with some of our practices and want to branch out and simplify and generate some things in a helpful way not just for me, but the hybrid-programmer web developers in the office.
One employee left us with a MVC site written in PHP, and I have had to maintain it a bit, and I get how it works but have my complaints, my main complaint is that it is tightly coupled with each piece dependent on another. I see the advantage of the seperation of concerns, but this would be confusing to anyone but me looking at the code.
So for example, if I need to add a new page to the site, I have to go add a view, and then add a model, then update the controller. The ad-hoc method of making a new page is way simpler than this and doesn't require a programmer.
My judgement was this was a much better method to build, rather then maintain, a website.
Still, it would be nice if I had some design patterns where I could reuse code effectively without it being dependent on a multiple places in the site.
So my question is, is there a design pattern for building and maintaining websites that is much more loosely-coupled? I'm not looking for slight variations on MVC, I need something quite different to look at, maybe some type of plugin approach.
EDIT:
Thanks for the answers so far! A different way of putting it is I want the code to be done better in my office. Do I A) Push for MVC or B) find/build an alternative not as confusing to the half-programmers. We already use classes for things like DB connectivity and Form helping. The point of this question was to explore B.
There's always a compromise between the code being confusing because it's highly deconstructionist, and the code being confusing because absolutely everything needed to do X is randomly scattered around a single file.
The problem with the latter is that exactly what is an "intuitive" way to split things up into monolithic modules differs between people. Highly decomposed and factored code is nearly always more difficult to wrap your head around, but once you do that, maintenance becomes both easy to do. I disagree that it would be confusing to anyone else but the author looking at it. Large-scope patterns like MVC are used because it becomes easier to spot them and work on projects structured around them over time.
Another advantage of using MVC is that you generally won't make the application more difficult to maintain for someone who comes after you if you don't stick to the layering. This is because now you have a predetermined place where to place any aspect of implementing a new feature.
As far as the tight coupling is considered, you can't really implement a feature without there being some connection between the layers. Loose coupling doesn't mean that the layers are ignorant of each other completely - it means that a layer should be unaware of how the other layers are implemented. E.g.: the controller layer doesn't care whether you're using a SQL database or just writing binary files to persist data at the data access layer, just that there is a data access layer that can get and store model objects for it. It also doesn't care about whether you use raw PHP or Smarty at the view layer, just that it should make some object available under some predetermined names for it. All the while the view layer doesn't even need to know there is a controller layer - only that it gets called with the data to display ready under the abovementioned names provided by /something/.
As frameworks templates go, I find the MVC pattern to be one of the most "loosely coupled" ways of building an application.
Think of the relationships like interfaces, or contracts between the parts of the application. The Model promises to make this data available to the View and the Controller. No one cares exactly how the Model does that. It can read and write from a typical DBMS, like MySQL, from flat files, from external data sources like ActiveResource, as long as it fulfills its end of the deal.
The Controller promises to make certain data available to the View, and relies on the Model to fulfill its promises. The view doesn't care how the Controller does it.
The View assumes that the Models and the Controllers will keep their promises, and can then be developed in a vacuum. The Model and Controller don't care if the view is generating XML, XHTML, JSON, YAML, plaintext, etc. They are holding up their end of the contracts.
And, of course, the View and the Controller need to agree that certain things exist. A View without some corresponding Controller activity might work fine, but could never be used. Even if the Controller doesn't do anything, as might be the case in static pages:
<?php
class StaticController extends ApplicationController
{
/**
* Displays our "about" page.
*/
public function about ()
{
$this->title = 'About Our Organization';
}
}
Then the associated View can just contain static text. (Yes, I have implemented things like this before. It's nice to hand a static View to someone else and say "Just write on this.")
If you look at the relationships between the M, V, and C as contracts or interfaces, MVC suddenly looks very "loosely coupled." Be wary of the lure of stand-alone PHP files. Once you start including and requiring a half-dozen .inc files, or mixing your application logic with your display (usually HTML) you may have coupled the individual pages more loosely, but in the process made a mess of the important aspects.
<?php
/**
* Display a user's profile
*/
require_once 'db.php';
$id = $db->real_escape_string($_GET['id']);
$user_res = $db->query("SELECT name,age FROM users WHERE id = $id;");
$user = $user_res->fetch_assoc();
include 'header.php';
?>
<h1><?php echo $user['name']; ?>'s Profile</h1>
<p><?php echo $user['name']; ?> is <?php echo $user['age']; ?> years old!</p>
<?php
include 'footer.php';
?>
Yeah, "profile.php" and "index.php" are completely unrelated, but at what cost?
Edit: In response to your edit: Push for MVC. You say you have "half-programmers," and I'm not sure which half (do you have front-end people who are good at HTML and CSS but not at server-side? writers with some programming experience?) but with an MVC framework, you can hand them just the views, and say "work on this part."
I have to say that I don't really see your problem with MVC, since your already using templates anyway. I kind of think of it as the pattern that evolves naturally when you try to add structure to an application.
When people first start developing PHP application, the code is usually one big mess. Presentation logic is mixed with business logic which is mixed with database logic. The next step that people usually take is to start using some kind of templating approach. Whether this involves a specialized template language such as smarty or just separating out the presentation markup into a separate file isn't really important.
After this most of us discovers that it's a good idea to use dedicated functions or classes for the database access logic. This really doesn't have to be any more advanced than creating specialized functions for each commonly executed query and placing all those functions in a common include file.
This all seems very natural to me, and I don't believe that it's very controversial. But, at this point you're basicly already using an MVC approach. Everything beyond this is just more or less sophisticated attempts to eliminate the need to rewrite commonly used code.
I understand that this might not be what to you wanted to hear, but I think you should re-evaluate MVC. There's a countless number of implementations, and if it's really the case that none of them suits your needs, then you could always write your own and more basic implementation.
Look at it this way: Since you're already using a template language you'll typically need to create first a regular PHP file, and then a templare file each time you create a new page. MVC doesn't have to be any more advanced than this, and in many cases it isn't. It might even be that all you really need to do is to investigate more sophisticated approaches for handeling data access and add it to your current system.
The fact that you have to create a new Model and Controller Action when you need a new page I don't think means that your M, V, and C layers are tightly coupled. This is just the separation of concerns and contributes to a decoupled system.
That said, it is quite possible to royally screw up the intent of MVC (and I've worked on an app like this) and make it make the components tightly coupled. For instance, a site might put the 'rendering engine' directly in the Controller layer. This would obviously add more coupling. Instead a good MVC will be designed so that the controller is only aware of the name of the view to use and then pass that name to the separate rendering engine.
Another example of bad design in an MVC is when the views have URLs hard-coded into them. This is the job of the Routing engine. The view should only be aware of the action that needs to be called and the parameter that action needs. The Routing engine will then provide the correct URL.
Zend framework is very loosely coupled and is very good. Check it out:
http://framework.zend.com
This article might be of use too:
http://blog.fedecarg.com/2009/02/22/zend-framework-the-cost-of-flexibility-is-complexity/
You can try code Igniter. Its very easy to learn and does not strictly adopt MVC whilst giving your code good structure.
Code Igniter and Kohana (a CI descendent) are OK, but also loosely MVC. I like the simple php framework. It doesn't get in your way and it provides the important stuff, without forcing a structure or complicated conventions on you.
Ah... good old MVC arguments.
I have to maintain a multi-faceted PHP application, pieces of which are written "MVC" style, but not all. Worse, different parts have different ways of doing MVC, all of which are homegrown. And some pages just do it all themselves.
The main problem is not that there is a diversity in framework code, but that the coders clearly did not understand how to abstract APIs. IMO, ths is the biggest problem with MVC frameworks. Almost all of the code I have to work with uses "models" as places to put functions. It is a nightmare to maintain.
To make this maintainable, IME you need a few things:
A distinct and well-defined data-access layer, the API boundary of which looks after retrieving and storing persistent storage and very little else.
I don't like to use the term "model" for that because that is contentious. Whatever calls that layer should not care how the data is stored, should not even be worrying about things like database handles: that is all the job of the data-access layer.
A dispatcher that is very light and doesn't do any magic outside of just dispatching.
Now you can put everything else in one place that accepts requests and parameters, probably normalised and error checked from the dispatcher, fetches the data (usually as objects) it needs, makes the changes it needs to do, saves the data it needs to, hands the data is needs to display to the view. Two hundred lines of code plodding through the task works for this step. You don't need to hive off bits into functions in another file that are called from nowhere else. You can even put the view on the end of this file! Idealism is nice to aspire to but pragmatism needs a look-in because this is maintainable.
(Okay, rant over... )
PHP's lack of enforcing a framework means that the best frameworks do what PHP does: they stay out of the way. Some of the most maintainable code I've worked on had a single require() statement at the top, did all the data-manipulation with data objects (no SQL in sight), then output HTML surrounded by template functions, with form control being done by a consistent function API.
Can someone please derive a concrete example from the following:
http://www.urdalen.com/blog/?p=210
..that shows how to deal with one-to-many and many-to-many relationships?
I've emailed the author some time ago but received no reply. I like his idea, but can't figure out how to implement it beyond simple single table relations.
Note: I don't want to use a full-blown ORM. I like doing my SQL by hand. I would like to improve the design of my app code though. Right now each domain object has its own class full of queries wrapped in static methods. They just return scalar, 1d-array (record) or 2d-array (recordset) depending on the query.
The problem of ORM's (The impedance mismatch, as it's called) is precisely with relations. In an object graph (In-memory objects), relationships are pointers to other objects. In a relational database, relationships are reversed; This makes it impossible to do a simple mapping between the two models, and that is why ORM's are so complex.
If you want to stay close to the database (avoiding an ORM), then you shouldn't try to abstract relationships away. The way I write datamappers is something like the following:
$car42 = $car_gateway->fetch(42);
$wheels = $wheel_gateway->selectByCar($car42);
In contrast to the ORM way:
$car42 = $car_gateway->fetch(42);
$wheels = $car42->selectWheels();
This does mean that the gateways end up in your client-code, but it also keeps things very transparent and close to the database.
If you're looking for a simple and portable DataMapper ORM, have a look at phpDataMapper. It's only dependencies are PHP5 and PDO, and it's very small and lightweight. It supports table relations and some other very nice features as well.
Given your response to Tom's answer, I would recommend that you look at something like Zend Framework. Its ORM has a take it or leave it architecture that can be implemented in stages.
When I came to my present employer, they had an application that had just been completed months previously but had been through one or two prior versions and the current version had been in development six months longer than it was supposed to have been. However, the code base was mess. For example there was no abstraction between the database access logic and the business logic. And, they wanted me to move the site forward building new functionality, extending existing features, and fixing existing bugs in the code. To further complicate things they weren't using any form of sanitation on data inputs or outputs.
As I started to wade into the problem, I realized that I would need a solution to abstract concerns that could be implemented in steps because they obviously weren't going to go for a complete rewrite. My initial approach was to write a custom ORM and DAL that would do the heavy lifting for me. It worked great because it didn't intrude on the existing code base, and so it allowed me to move entire portions of the application to the new architecture in an unobtrusive manner.
However, after having ported a large portion of the user's area of our site to this new structure and having built an entire application on my custom framework (which has come to also include a custom front-end controller and mvc implementation), I am switching to Zend Framework (this is my choice though I am certain that some of the other frameworks would also work in this situation).
In switching to the Zend Framework I have absolutely no concerns about the legacy code base because:
I can build new models and refactor
old models (built on my custom
framework) unobtrusively.
I can refactor the existing
controllers (such as they are) to be
wrapped within a class that behaves
in a manner consistent with Zend's
MVC framework so that it becomes a
minor issue to actually begin using
Zend's Front-End Controller.
Our views are already built in
Smarty so I don't have to worry
about separating controller and view
logic, but I will be able to extend
the Zend Framework so that I can
render existing templates in Smarty
while building new templates in
straight PHP.
Basically, Zend Framework has a take it or leave architecture that makes its a joy to use within existing projects because new code and refactored code doesn't need to intrude on existing code.
Recently thanks to rails' popularity, many people start using activerecord as model. however, before I heard of rails (my peer group was not a fan of open source stuff, we were taught in a .NET school...) and while I was doing my final year project, i found this definition for a model
The model represents enterprise data and the business rules that govern access to and updates of this data. Often the model serves as a software approximation to a real-world process, so simple real-world modeling techniques apply when defining the model.
it doesn't say the model should represent one table as what activerecord does. And normally within a transaction, one may have to query a few unrelated tables and then manipulate data from different tables... so if activerecord is used as model, then either one would have to cram all the logic code into the controller (which is kinda popular in some php frameworks) that makes it difficult to test or hack the activerecord model so that it performs database operation on not only the table it maps to, but also other related tables as well...
so, what is so great about abusing (IMHO) activerecord as the model in a MVC architectural pattern?
Martin Fowler described this pattern in Patterns of Enterprise Application Architecture together with two other patterns or architectures. These patterns are good for different situations and different amounts of complexity.
If you want to so only simple stuff you can use Transaction Script. This is an architecture you saw in lot's of old ASP and PHP pages where a single script contained the business logic, data-access logic and presentation logic. This falls apart fast when things get more complicated.
The next thing you can do is add some separation between presentation and model. This is activerecord. The model is still tied to the database but you've a bit more flexibility because you can reuse your model/dataccess between views/pages/whatever. It's not as flexible as it could be but depending on your data-access solution it can be flexible enough. Frameworks like CSLA in .Net have a lot of aspects from this patterm (I think Entity Framework looks a bit too much like this too). It can still handle a lot of complexity without becoming unmaintainable.
The next step is separating your data-access layer and your model. This usually requires a good OR mapper or a lot of work. So not everyone wants to go this way. Lot's of methodologies like domain driven design perscribe this approach.
So it's all a matter of context. What do you need and what is the best solution. I even still use transaction-script sometimes for simple single use code.
I've said many times that using Active Record (or ORM which is almost the same) as Business Models is not a good idea. Let me explain:
The fact that PHP is Open Source, Free (and all that long story...) provides it with a vast community of developers pouring code into forums, sites like GitHub, Google code and so on. You might see this as a good thing, but sometimes it tends not to be "so good". For instance, suppose you are facing a project and you wish to use a ORM framework for facing your problem written in PHP, well... you'll have a lot of options to choose for:
Doctrine
Propel
QCodo
Torpor
RedBean
And the list goes on and on. New projects are created regularly. So imagine that you've built a full blown framework and even a source code generator based on that framework. But you didn't placed business classes because, after all, "why writing the same classes again?". Time goes by and a new ORM framework is released and you want to switch to the new ORM, but you'll have to modify almost every client application using direct reference to your data model.
Bottom line, Active Record and ORM are meant to be in the Data Layer of your application, if you mix them with your Presentation Layer, you can experience problems like this example I've just laid.
Hear #Mendelt's wise words: Read Martin Fowler. He's put many books and articles on OO design and published some good material on the subject. Also, you might want to look into Anti-Patterns, more specifically into Vendor Lock In, which is what happens when we make our application dependent on 3rd party tools. Finally, I wrote this blog post speaking about the same issue, so if you want to, check it out.
Hope my answer has been of any use.
The great thing about using the Rails ActiveRecord as a model in MVC is that it gives you an automatic ORM (Object Relational Mapper) and easy way to create associations between models. As you have pointed out, MVC can sometimes be lacking.
Therefore, for some complex transaction involving many models, I'd suggest to use a Presenter in between your controller and your models (Rails Presenter Pattern). The Presenter would aggregate your models and transactional logic and would remain easily testable. You definitely want to strive to keep all of your business logic in your models or presenters, and out of your controllers (Skinny Controller, Fat Model).