I've recently started to work on an application based on the Kohana framework that has been in development for some months, and the code is not using any DB transactions. I've already seen data corruption because of this.
Going through all the code to add transactions manually would be a lengthy and error-prone task, so my plan is to implement something like the declarative transactions in the Java EE and Spring frameworks: simply wrap every controller action in a DB transaction using the before() and after() functions of the project-specific controller superclass. Maybe make it configurable via an overridable property containing the names of actions that require a transaction.
Has this been done before in a reusable way?
How can I deal with exceptions? Is after() called when an exception occurs? If so, how can I find out whether an exception was thrown? If not, how else can I react to them?
It doesn't look like Kohanas ORM has transaction suport but you can still makes the calls manually
$db->query("START TRANSACTION");
$db->query(query1);
$db->query(query2);
$db->query("COMMIT");
I had a look and it seems like the after method is not called if there is an exception, if you have a look in here and search for 'after' you get to some code where you see the catch statement doesn't run the after() method.
Im really not sure what how you can make it global without other trade-offs...
Using the before() and after() functions of the controller base class worked for many cases, but I felt uneasy about not doing an explicit rollback when dealing with exceptions. Then I found that I was also getting an implicit rollback whenever an action called Request::current()->redirect() because that function does anexit, so after() never gets called.
My new and improved solution is to override Kohana's Request class, and I create a Kohana module so everyone can use it easily:
https://github.com/brazzy/kohana-transactional
Related
In my controller I'm using MyModel::where(...) which is calling the database during one of my tests. I thought that I'd be able to overload it with the following but it's still querying the database. What can I do to be sure my test is still requiring that ::where is called but returns a mocked model so I can resume testing the rest of this controller method?
MyModel::shouldReceive('where')
->once()
->with('param1', 'param2')
->andReturn($mockedModel);
First of all, you have to understand why you can't mock the where method. That's because the class Illuminate\Database\Eloquent\Model (Eloquent is its alias) doesn't have a where method explicitly declared. This method is called through the magic methods __callStatic and __call and is, in fact, a method of Illuminate\Database\Eloquent\Builder (sources).
Then, you have several options :
You can accept to call your database in your tests and just define a real context before your assertions. Your tests will be less unitary but we can deal with that in most of the cases.
#michaelcurry solution is a good one. You can build your own abstraction layer with query scopes or other architecture (injecting the query builder by yourself in the model for example) to produce a more testable code.
[never tried] You can mock directly the DB facade to completely bypass the database. You will need a good understanding of the Laravel core but it could be a good way to write "pure" unit tests.
Anyway, don't hesitate to dive into the Laravel source code. The code is clear and there are just few classes really important. That's essential to really exploit the power of the framework.
Use http://laravel.com/docs/eloquent#query-scopes and create a new function that just returns the data you want.
If you have not read it "Laravel: From Apprentice To Artisan" I would suggest it. Will help you understand how all of this is structured.
I'm working on a PHP/MySQL app using the Yii framework.
I've come across the following situation:
In my VideoController, I have a actionCreate which creates a new Video and actionPrivacy which sets the privacy on the Video. The problem is that during the actionCreate the setPrivacy method of the Video model is called which currently has a transaction. I would like the creation of the Video to be in a transaction as well which leads to an error since a transaction is already active.
In the comment on this answer, Bill Karwin writes
So there's no need to make Domain Model classes or DAO classes manage
transactions -- just do it at the Controller level
and in this answer:
Since you're using PHP, the scope of your transactions is at most a
single request. So you should just use container-managed transactions,
not service-layer transa. That is, start the transaction at the start
of handling the request, and commit (or rollback) as you finish
handling the request.
If I manage the transactions in the controller, I would have a bunch of code that looks like:
public function actionCreate() {
$trans = Yii::app()->getDb()->beginTransaction();
...action code...
$trans->commit();
}
That leads to duplicated code in a lot of places where I need transactions for the action.
Or I could refactor it into the beforeAction() and afterAction() methods of the parent Controller class which would then automatically create transactions for each action being performed.
Would there be any problems with this method? What is a good practice for transaction management for a PHP app?
The reason that I say transactions don't belong in the model layer is basically this:
Models can call methods in other models.
If a model tries to start a transaction, but it has no knowledge of whether its caller started a transaction already, then the model has to conditionally start a transaction, as shown in the code example in #Bubba's answer. The methods of the model have to accept a flag so that the caller can tell it whether it is permitted to start its own transaction or not. Or else the model has to have the ability to query its caller's "in a transaction" state.
public function setPrivacy($privacy, $caller){
if (! $caller->isInTransaction() ) $this->beginTransaction();
$this->privacy = $privacy;
// ...action code..
if (! $caller->isInTransaction() ) $this->commit();
}
What if the caller isn't an object? In PHP, it could be a static method or simply non-object-oriented code. This gets very messy, and leads to a lot of repeated code in models.
It's also an example of Control Coupling, which is considered bad because the caller has to know something about the internal workings of the called object. For example, some of the methods of your Model may have a $transactional parameter, but other methods may not have that parameter. How is the caller supposed to know when the parameter matters?
// I need to override method's attempt to commit
$video->setPrivacy($privacy, false);
// But I have no idea if this method might attempt to commit
$video->setFormat($format);
The other solution I have seen suggested (or even implemented in some frameworks like Propel) is to make beginTransaction() and commit() no-ops when the DBAL knows it's already in a transaction. But this can lead to anomalies if your model tries to commit and finds that its doesn't really commit. Or tries to rollback and has that request ignored. I've written about these anomalies before.
The compromise I have suggested is that Models don't know about transactions. The model doesn't know if its request to setPrivacy() is something it should commit immediately or is it part of a larger picture, a more complex series of changes that involve multiple Models and should only be committed if all these changes succeed. That's the point of transactions.
So if Models don't know whether they can or should begin and commit their own transaction, then who does? GRASP includes a Controller pattern which is a non-UI class for a use case, and it is assigned the responsibility to create and control all the pieces to accomplish that use case. Controllers know about transactions because that's the place all the information is accessible about whether the complete use case is complex, and requires multiple changes to be done in Models, within one transaction (or perhaps within several transactions).
The example I have written about before, that is to start a transaction in the beforeAction() method of an MVC Controller and commit it in the afterAction() method, is a simplification. The Controller should be free to start and commit as many transactions as it logically requires to complete the current action. Or sometimes the Controller could refrain from explicit transaction control, and allow the Models to autocommit each change.
But the point is that the information about what tranasction(s) are necessary is something that the Models don't know -- they have to be told (in the form of a $transactional parameter) or else query it from their caller, which would have to delegate the question all the way up to the Controller's action anyway.
You may also create a Service Layer of classes that each know how to execute such complex use cases, and whether to enclose all the changes in a single transaction. That way you avoid a lot of repeated code. But it's not common for PHP apps to include a distinct Service Layer; the Controller's action is usually coincident with a Service Layer.
Best Practice: Put the the transactions in the model, do not put the transactions in the controller.
The primary advantage of the MVC design pattern is this: MVC makes model classes reusable without modification. Make maintenance and implementing new features easy.
For example, presumably you are primarily developing for a browser where a user enters one collection of data at a time, and you move data manipulation into the controller. Later you realize you need to support allowing the user to upload a large number of collections of data to be imported on the server from the command line.
If all the data manipulation was in the model, you could simply slurp in the data and pass it to the model to handle. If there is needful (transactional) functionality in the controller, you would have to replicate that in your CLI script.
On the other hand, perhaps you end up with another controller that needs to perform the same functionality, from a different point. You will need to replicate code in that other controller as well now.
To that end, you merely need to solve the transaction challenges in the model.
Assuming you have a Video class (model) with the setPrivacy() method that already has transaction build in; and you want to call it from another method persist() which needs to also wrap its functionality in a larger transaction, you could merely modify setPrivacy() to perform a conditional transaction.
Perhaps something like this.
class Video{
private $privacy;
private $transaction;
public function __construct($privacy){
$this->privacy = $privacy;
}
public function persist(){
$this->beginTransaction();
// ...action code...
$this->setPrivacy($this->privacy, false);
// ...action code...
$this->commit();
}
public function setPrivacy($privacy, $transactional = true){
if ($transactional) $this->beginTransaction();
$this->privacy = $privacy;
// ...action code..
if ($transactional) $this->commit();
}
private function beginTransaction(){
$this->transaction = Yii::app()->getDb()->beginTransaction();
}
private function commit(){
$this->transaction->commit();
}
}
In the end, your instincts are correct (re: That leads to duplicated code in a lot of places where I need transactions for the action.). Architect your models to support the myriad of transactional needs you have, and let the controller merely determine which entry point (method) it will use in it's own context.
No you are right. The transaction is delegated by the "create" method which is what a controller is supposed to do. Your suggestion of using a 'wrapper' like beforeAction() is the way to go. Just make the controller extend or implement this class. It looks like you are looking for an Observer type pattern or a factory-like implementation.
Well, one disadvantage of these broad transactions (over the whole request) is that you limit concurrency capabilities of your database engine and you also increase deadlocks probability. From this point of view, it might pay off to put transactions only where you need them and let them cover only code that needs to be covered.
If possible, I would definitely go for placing transaction in models. The problem with overlapping transactions can be solved by introducing BaseModel (ancestors of all models) and variable transactionLock in that model. Then you simply wrap your begin/commit transaction directives into BaseModel methods that respect this variable.
Do you think it's a good idea?
Let's say you have an application component that is used by other components to retrieve / update data in the db. It's basically a class with get(), set(), update() methods.
Would it be a good idea for that component to update (or set) data only in it's properties when called, and on __destruct to update the db as well? Or should it directly update the db on each set/ update call ?
Updating the database on object destruction smells to me a little bit like a software side effect. That is, an action that takes place in an unexpected and somewhat non-explicit place. It would not be obvious from viewing your code that a database action is happening when __destruct() is called, even if you call it explicitly. Future code maintainers (including yourself) could be easily confused when trying to hunt down a bug involving inconsistent data, but not seeing any calls to the database or method calls resembling data interactions when viewing the code.
I would advise against it.
Attempting to throw an exception from a destructor (called in the time
of script termination) causes a fatal error.
So what about when you have an exception? Any way, I think this is not a good idea, you can't control the work flow, and it is easy to lead a debug hell.
I'd like to use a transaction rollback method to isolate my database for unit testing purposes. Ideally, I would use a structure something like this:
public static function setUpBeforeClass(){
Mage_Core_Model_Resource_Transaction::beginTransaction();
}
public function testOne(){...}
public function testTwo(){...}
public static function tearDownAfterClass(){
Mage_Core_Model_Resource_Transaction::rollBack();
}
Unfortunately the Mage_Core_Model_Resource_Transaction class doesn't expose public begin/rollbackTransaction functions. I can't find any public Magento functions to meet the requirement. Is there a Zend equivalent that will work?
I guess I could rewrite Mage_Core_Model_Resource_Transaction and add public wrappers for the protected methods, but I'm hesitant to override such a core class.
I have also tried using
$this->model = Mage::getModel('model_being_tested');
$this->model->getResource()->beginTransaction();
...
$this->model->getResource()->rollBack();
and then use the $this->model in the tests but that can't be used in static functions.
Any suggestions or alternative architectures? Thanks in advance.
Frankly speaking, I am going to create some kind of test suite for Magento to make possible writing test cases in your module without taking care of the initialization and so on. Of course I don't have enough time because of the projects, but I want to share the idea wich I am going to use considering database fixtures. I came up with creating a separate database for unit tests (copied from current by test case), because each test case will load initial data for it via fixture. The database connection credentials will be set up prior to Mage::app()->run(), so it will be possible to protect your development copy from possible changes by unit tests.
You cannot rely on transactions, especially in case of saving product... There is set a commit callback for starting re-indexing process and it may cause unpredictable results if you haven't committed data to product tables. As well mysql server may gone away in this case, especially if you have a large database.
UPDATE:
The extension for it:
http://www.ecomdev.org/2011/02/01/phpunit-and-magento-yes-you-can.html
I asked around on various IRC channels but was unable to get an answer with a definitive explanation behind it. Should errors (pertaining to the model, such as transaction failures) be handled in the model, or in the controller?
Thanks in advance for any help.
EDIT
Well, the confusing thing is that my code (in the model) looks something like this already:
try
{
// Connect to MongoDB
// Fetch a record
}
catch (MongoConnectionException $e)
{
// Handle this error
}
catch (MongoException $e)
{
// Handle this error
}
So, should I return exceptions based on the exceptions MongoDB returns? Or should I directly allow these exceptions to bubble up to the controller?
Thanks!
The correct answer is "Both", but mostly in the Model.
The appropriate thing for your controller to do is simply catch some exception that the model throws, and handle outputting a nice "whups" message. Depending on how you're structuring your models, it might be appropriate for the controller to do some logging.
Anything other than catching the exception, maybe writing to a log (if your model infrastructure doesn't do it), and displaying a pretty error, does not belong in your controller.
Errors such as a transaction failure - and what to do in such cases - are business logic issues. Thus, they should be handled in the model and appropriate notifications passed back up to the controller.
Fat model, skinny controller.
In most cases, you should throw or pass exceptions to the caller/receiver AKA Controller or BLL.
It's controller's job to process actions not model
It's view's job to display a message box(or whatever) not model
You can't HANDLE exceptions in model, for real... you can only log or throw them.
Scott Guthrie for ASP.NET C# suggests using the Controller as the exception handler. He also, suggests setting up helper objects and handlers for the project. This in turn allows you to continue your development as normal.
Note, however with PHP MVC is still in its earliest stages and implementation so, this may not be perfect.
I do think that once you have decided how to handle the solution that you still with it and follow that pattern once you have made the decision.
The ideas behind MVC frameworks are quite simple and extremely flexible. The idea is that you have a single controller (such as index.php) that controls the launch of applications within the framework based on arguments in the request. This usually includes, at a minimum, an argument defining which model to invoke, an event, and the usual GET arguments. From there the controller validates the request (authentication, valid model, request sanitization, etc.) and runs the requested event.
At present the are only really two true frameworks...and this could be a problem for coding, support and future releases. Though, there are alot of frameworks that extend themselves to support MVC.
By Earliest stages, I mean to say and suggest that the current frameworks, solutions and support is limited as a result of rushed deliveries and poor documentation. Additionally, I'm suggesting what works for me and has worked for me in the past.
I would strongly This Website