To begin: i'm not an expert in Zend Framework and doing something terrible wrong. I'm sure of that. I think there's something wrong with my design patterns.
As an example:
I'm building access management with Zend_ACL (Access Control List)
There are three tables in the database:
roles
resources
permissions
the permissions table handles the role-resources relation.
I made a model for each table, it extends Zend_Db_Table_Abstract. So far so good.
Now in the ACL I load the resources, role and permissions on a page request and add it to the ACL.
Now the part I'm doing something wrong: The way I do it is call methods from the tablemodels that give me the required data. But when I look at my profiler it takes 117 select queries and takes 0.7 seconds just to load the ACL. No queries for the underlying system yet. This can't be good and I'm sure there is a better way. I just can't find anything about this on google or anywhere.
Is there someone who can tell me if I'm doing something wrong and if I am, what I should to to speed it up? Should I load everything in one query to models and let them handle it? How do I do that, are there any examples?
Thanks in advance!
It's not clear exactly what you're doing, but you shouldn't be grabbing generating so many queries.
Some things to think about:
Do you need to load the full ACL on every request? Perhaps you could structure your data-access layer in such a way that it checks for relevant permissions when a user attempts to perform some action.
If you do need the full ACL, you ought to be able to construct a single query to grab all the relevant data. Perhaps eschewing the Zend_Table stuff here and just executing a raw custom query? Not exactly a best-practice, but if you're loading everything, it's just one query.
More concrete code in your question would definitely produce more concrete answers.
If you do need to load all the stuff, why not load it just once and serialize your acl object and keep it in session or cache ?
Every time you need to access your ACL object, check if the cache or the session variable is set or not. If it is set, you can just avoid running the queries and just unserialize the object from session or cache.
I agree this is a dirty solution, but it works and will not run the 117 queries each time.
Related
I remember reading something similar about Symfony's Doctrine, but I can't find anything about this in Zend 2 documentation.
Here is the question explained:
Let's say that in a single controller action I call two model functions (both in same model): both functions run exactly the same TableGateway query sets. These queries only SELECT data. Furthermore there are no INSERT/UPDATE operations anywhere in this action.
In this case, would Zend run the query sets twice? Or, seeing that they are duplicated and no INSERT/UPDATE operation is done in-between, it would run the query set just once, and the second time return it from some internal cache.
ps. Just in case, please understand that I don't need general best practice advice, just the specific answer from someone who knows the depths of Zend's core.
No. It would be bad to do that anyway as your application has no idea if another application has written to the database and invalidated your cached query.
If you have query cache enabled in my sql then the query could be cached as the rdbms knows if that data can be cached or if it's been changed.
I have rather theoretical question about proper approach of using Symfony though I believe the approach is to be same for any other PHP framework.
I have tariff objects stored in database. I want to provide a cost model for each tariff basing on user input.
My initial approach was to create an array, one's each element would contain data from corresponding tariff object and calculated data. All of that was done in controller's action method.
Later I have created another class CostModel and then created an array CostModel[], which than was passed to $this->render() method. Again it's done in controller.
This approach works well enough. However, since I have not much experience with Symfony, I have doubts that this approach - performing calculations in controller - is good one.
Is there any better way for this?
Well, your question could have more than one answer as it is very opinion-based.
What I can say without any doubt about controller's code is that the less it is the more is good. Why I said that? Because controller code isn't reusable, because controllers are made to "connected" views and business logic (keep attention: connect, not encapsulate) and a general rule that I follow when I develop with Symfony2, is to write, into controller, lines of code for "objects" that are directly accessible from controller (form, request, views, and so on); all code that isn't related to these concepts should be migrated elsewhere.
Your solution is a good starting point but we cannot judge as we haven't more details and we don't know the architecture of your software. What can I say - and I hope you already know - is that you can pass to render (so to view templating system; I suppose you are using twig) directly the ArrayCollection you've obtained querying the database (so basically you don't neeed CostModel[] array). So, maybe, your approach is good but not the best: maybe you can take advantage of Repository facility, write a good query that can extract and calculate data for you (in a more optimized way) and use repository directly into controller. That way you could at the same time, migrate code where it should stay, write less number of code lines, do some optimitazion (or better, let Doctrine do for you) and you don't need to create a brand new class (model).
I'm new to CodeIgniter but want to perform best practices from the start. I have a simple authorization call that needs to be able to be called from several controllers. Hence I'm thinking it should be placed in either a library or a helper function. The call would take the user's id and a required authorization "level", grab their information from the DB, make sure they have that level of access, and return true or false.
Let's say:
auth($user,5)
My first instinct is to make this a library, but it seems odd to place it directly in a library because there are DB calls, which I would think should go in a model. It appears that only the Session library contains calls directly to the DB (for when database session storing is turned on).
So, I could access the DB directly within the library, or try to link to an external Model. Looking it up on the web, I'm only finding people who have trouble with both routes. Before I dive too deeply into getting one of them to work, I'd appreciate any opinions out there on how to go about this.
Thanks,
Jeremy
It seems like that is a model function. At least put it there until later in development.
If you later find there is a need for multiple models which would require duplicating the function, then would be a good time to move it to a helper or library.
i am developing a groupon-like system, and i came into this project when the system is already around 70% built, and it was built using cakePHP, to be honest, i know nothing about cakePHP. and i came across this:
a member bought a deal
if(has_enough_account_balance){
if((parameters validated)){
insert into 'deal_user' table
log transaction
update 'deal' table by:
user_count = current user_count + bought deal //to determine whether this deal is tipped or not
if(this deal is tipped){
issue coupon
}
}
}else{
this_user_owed
}
the admin confirmed that a particular user has paid his/her owed deal payment
confirm has_paid
update into 'deal_user' table
log transaction
update 'deal' table by:
user_count = current user_count + bought deal //to determine whether this deal is tipped or not
if(this deal is tipped){
issue coupon
}
now seeing that the two of those has something in common, i am trying to do this:
a member bought a deal
if(has_enough_account_balance){
if((parameters validated)){
process_deal(parameters)
}
}else{
this_user_owed
}
on admin confirmation:
confirm has_paid
process_deal(parameters)
and process_deal would be:
function process_deal(parameters){
if(isset(deal_id)){
update into 'deal_user' table
}else{
insert into 'deal_user' table
}
log transaction
update 'deal' table by:
user_count = current user_count + bought deal //to determine whether this deal is tipped or not
if(this deal is tipped){
issue coupon
}
}
is it possible to do things like this ? and where is the best place should i put this process_deal method, i have tried to put this inside the app_controller class, but it seems that it wont update the table, i am not sure why can't it update (i am using the updateAll method), thank you very much
Looks like you can have this function implemented into /models/deal_user.php or /models/deal.php as model classes, so you can share the process_deal across needy controllers.
When a controller need it, simply include the ModelClass. Fatter Models.
and furthermore, you should not include this function into your app_controller as it might not make sense sharing this method across all other controllers, or instantiating the DealUser and Deal models across all controllers, and some might not need it at all.
If you tried debug($this) inside a controller, you know how horrible the array is. The more Model you include, the messier it will be.
Update
(Base on personal experience), put your code into following files when..
/app/bootstrap.php
Is when you have shared codes among everywhere in your application, like debug, json_encode (when PHP version < 5.2) etc
/app/controllers/components/*.php
Is when you have shared code/logic among controllers, with very minimal DB interaction.
/app/models/*.php
(Fat Models) Is when you have shared functionalities among a few controllers, that manipulate the data before saving into the DB, example such as your question above.
/app/libs/*.php
Is when you have shared external codes that doesn't fit into MVC, like TwitterOauth, or other generic classes/packages, which does not make sense converting it into Components, or too complex to do so. Import them into cake by using App::import('Lib', <name>) is pretty sufficient and neat.
/app/views/helpers/*.php
Is when you have shared html codes to render, that required some logic before converting it from data into html codes.
/app/app_controller.php
Is only when you need to do some hack on beforeFilter, beforeRender etc, that need to have common functionalities among controllers. The reason is because, your *_controller is extending app_controller, and if you are tempted and then add shared components, uses, helpers in the app_controller with the hope of having them in all controllers, it turns out that you have heavy controllers in every requests. Make it a habit try make your app_controller as slim as possible. Write more codes in each controllers.
/app/app_model.php
Same idea goes for app_controller
/app/plugins/*/
Is when you include external cake-like framework/features, or you can wrap certain framework into a plugin if you want.
/app/webroot/*/
And never never put your code under webroot! It's not neat, break Cake's structure, and it's not recommended! It's evil. Considering moving your code out of Cake if they couldn't be fit into Cake.
I think that is all? Again, they are my personal experience base on coding experience. Do comment/edit if you think they are not correct.
I'd go with Lionel Chan's answer above.
Also, it would probably help you to read over the CakePHP documentation. What you're trying to do looks like it shouldn't be difficult, but you'll have much more luck if you learn the framework and work within it rather than trying to work around it just using what you already know.
Any code that deals with a certain model's database table should go in that particular model class (the fat models referred to by Lionel).
So you could put the process_deal method in the Deal model.
The DealsController can then access the method like this:
$this->Deal->process_deal().
Then, if there's a relationship between two models, for example: DealUser hasMany Deal, you can access the method from within an action in the DealUsersController like this:
$this->DealUser->Deal->process_deal();
If there isn't a relationship between the models, you could still call the process_deal() method from within an action in the DealUsersController like this:
$this->loadModel('Deal');
$this->Deal->process_deal();
I hope this helps point you in the right direction, but I'd still recommend spending an hour or two perusing the CakePHP documentation linked to above because you're likely to find the answers to most of your questions there...
P.S. Kudos for going for the modular approach. It's always refreshing to see...
I've been reading several articles on MVC and had a few questions I was hoping someone could possibly assist me in answering.
Firstly if MODEL is a representation of the data and a means in which to manipulate that data, then a Data Access Object (DAO) with a certain level of abstraction using a common interface should be sufficient for most task should it not?
To further elaborate on this point, say most of my development is done with MySQL as the underlying storage mechanism for my data, if I avoided vendor specific functions -- (i.e. UNIX_TIMESTAMP) -- in the construction of my SQL statements and used a abstract DB object that has a common interface moving between MySQL and maybe PostgreSQL, or MySQL and SQLite should be a simple process.
Here's what I'm getting at some task, are handled by a single CONTROLLER -- (i.e. UserRegistration) and rather that creating a MODEL for that task, I can get an instance of the db object -- (i.e. DB::getInstance()) -- then make the necessary db calls to INSERT a new user. Why with such a simple task would I create a new MODEL?
In some of the examples I've seen a MODEL is created, and within that MODEL there's a SELECT statement that fetches x number of orders from the order table and returns an array. Why do this, if in your CONTROLLER your creating another loop to iterate over that array and assign it to the VIEW; ex. 1?
ex. 1: foreach ($list as $order) { $this->view->set('order', $order); }
I guess one could modify the return so something like this is possibly; ex. 2.
ex. 2: while ($order = $this->model->getOrders(10)) { $this->view->set('order', $order); }
I guess my argument is that why create a model when you can simply make the necessary db calls from within your CONTROLLER, assuming your using a DB object with common interface to access your data, as I suspect most of websites are using. Yes I don't expect this is practical for all task, but again when most of what's being done is simple enough to not necessarily warrant a separate MODEL.
As it stands right now a user makes a request 'www.mysite.com/Controller/action/args1/args2', the front controller (I call it router) passes off to Controller (class) and within that controller a certain action (method) is called and from there the appropriate VIEW is created and then output.
So I guess you're wondering whether the added complexity of a model layer -on top- of a Database Access Object is the way you want to go. In my experience, simplicity trumps any other concern, so I would suggest that if you see a clear situation where it's simpler to completely go without a Model and have the data access occur in the equivalent of a controller, then you should go with that.
However, there are still other potential benefits to having an MVC separation:
No SQL at all in the controller: Maybe you decide to gather your data from a source other than a database (an array in the session? A mock object for testing? a file? just something else), or your database schema changes and you have to look for all the places that your code has to change, you could look through just the models.
Seperation of skillsets: Maybe someone on your team is great at complex SQL queries, but not great at dealing with the php side. Then the more separated the code is, the more people can play to their strengths (even more so when it comes to the html/css/javascript side of things).
Conceptual object that represents a block of data: As Steven said, there's a difference in the benefits you get from being database agnostic (so you can switch between mysql and postgresql if need be) and being schema agnostic (so you have an object full of data that fits together well, even if it came from different relational tables). When you have a model that represents a good block of data, you should be able to reuse that model in more than one place (e.g. a person model could be used in logins and when displaying a personnel list).
I certainly think that the ideals of separation of the tasks of MVC are very useful. But over time I've come to think that alternate styles, like keeping that MVC-like separation with a functional programming style, may be easier to deal with in php than a full blown OOP MVC system.
I found this great article that addressed most of my questions. In case anyone else had similar questions or is interested in reading this article. You can find it here http://blog.astrumfutura.com/archives/373-The-M-in-MVC-Why-Models-are-Misunderstood-and-Unappreciated.html.
The idea behind MVC is to have a clean separation between your logic. So your view is just your output, and your controller is a way of interacting with your models and using your models to get the necessary data to give to the necessary views. But all the work of actually getting data will go on your model.
If you think of your User model as an actual person and not a piece of data. If you want to know that persons name is it easier to call up a central office on the phone (the database) and request the name or to just ask the person, "what is your name?" That's one of the ideas behind the model. In a most simplistic way you can view your models as real living things and the methods you attach to them allow your controllers to ask those living things a series of questions (IE - can you view this page? are you logged in? what type of image are you? are you published? when were you last modified?). Your controller should be dumb and your model should be smart.
The other idea is to keep your SQL work in one central location, in this case your models. So that you don't have errant SQL floating around your controllers and (worst case scenario) your views.