Can I pass arguments into a model function? - php

Is it even possible to do something like this in codeigniter?Is it considered a good practice in general?

Yes it is possible.
I do it all of the time, if the data is not from post I can send it to the model.
$this->load->model('some_model');
$this->some_model->some_function($var);
What data do you want to send to the model? This will help in deciding whether it is bad practice.

Ofcourse it is possible! Like Kieran showed, you can just write functions in your model class that accepts parameters.
Just keep in mind that your controller should first parse/validate these parameters, no extra validation should be included in your model. Your model should be strictly used to write to and read from the DB.
So, as a good practice, you should only pass arguments that contain actual data to be stored or used in your queries.
I usually write different functions for different WHERE cases. For example:
select($id){}
select_by_name($name){}
...
This keeps your model comprehensible. The only exception I make is in the case of limiting your result. In all my models, I have on function to select all records, with an option to pass two more variables for pagination purposes:
select_all($start=null,$limit=null){
$qry = "SELECT * FROM ...";
if($start != null) $qry .= " LIMIT ?,?";
return $this->db->query($qry,func_get_args());
}

Related

PHP models : one or more setters by database action?

I'm using the MVC pattern in a CodeIgniter PHP project and I'm wondering what's the best way to build methods of the models in order to ensure readability of the code and scalability.
In short, is this better to do this ? :
public function set_account_state($new_state) {
// UPDATE a database record state to $new_state [0, 1 or 2]
}
Or this ? :
public function reject_account() {
// UPDATE a database record state to 0
}
public function accept_account() {
// UPDATE a database record state to 1
}
public function pending_account() {
// UPDATE a database record state to 2
}
Or maybe another way ?
Also, is there a good practice for function naming in such cases ?
function set_account_state($state)
This is better when you have only one task and that is to change the state.
But if in future you might have to do different tasks before you change the account state then you need three different methods.
You can still have that original method to change the state of the account and call it from your three methods.
Function naming should include a verb with nouns to make it clearer.
function pending_account() is not that clear, function keep_account_pending() is I feel a better way to name it.
In my opinion, the second way is the better way.
Using names that indicate what will the method do can improve the readability of the code.
As your code grows bigger, it's easy to forget what number represents what state. That does not happen with names because a method called reject_account() will indicate that account will be rejected better then a number passed as argument to a function.
Also when other people work on your code, it will be easier for them to understand what is going on when a method is called. Having said that, it's better for one understand that the client account is accepted when one sees a method called accept_account() then when one sees set_account_state(1).

PHP MVC, PDO: loop through returned mysql data

In my DB layer class I have a method that takes a select sql query as param:
public function select($sqlQuery) {
$stmt = $this->pdo->prepare($sqlQuery);
$stmt->execute();
$ret = $stmt->fetch(PDO::FETCH_ASSOC);
return $ret;
}
I want to be able to return the fetched data to my model, and then loop through it in my view class, without using any PDO in the view.
How should this be done the right way?
I want to be able to reach the table rows as $row['testCol'] etc.
Call fetchAll() instead of fetch(), which will return a full multidimensional array.
public function select($sqlQuery) {
$stmt = $this->pdo->prepare($sqlQuery);
$stmt->execute();
$ret = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $ret;
}
$results = $your_object->select("SELECT * FROM yourtable");
// It's a 2D array!
var_dump($results);
Model is a layer. Not a class or object.
And, if you write your code by adhering to SRP, the model layer would contain several different types of structures. As minimum you would have separate classes for domain business logic and storage abstraction.
What you have here is complete mess. Not only you lumping both domain logic and storage mechanism in same class (and pretend that it is "model"), but you are also exposing SQL directly to presentation layer.
If you were trying to build a proper model layer, the current view would request information from service instance (which would be the type of structures, that one would use to separate domain business logic from views and controller) and acquire the data it need.
The service would instantiate the proper domain object(s) and, based on state, previously set by controller, retrieve information into them using some form of storage abstraction (either data mapper, repository, unit of work, dao or some similar structure). Then, after performing required data manipulations on the filled domain object(s), it would return data to the view.
View, based on received information and previous signals from controller, would decide what sort of response should be generated. If the indicated for of response is HTML, it would use one or (usually) multiple templates to compose the output. Thought it might as well send just a HTTP location header, to trigger a redirect.
For further reading I would recommend this answer.
The point is: you currently have simplified the concepts of MVC to the point where even separation between presentation and model layer has been broken.
You could easily return $stmt. It allows you to iterate over it and is accessible like PDO::FETCH_ASSOC:
$rows = $model->select($sqlQuery);
...
foreach($rows as $row)
{
echo $row['testCol'], "\n";
}
Inside your function it's like:
public function select($sqlQuery) {
$stmt = $this->pdo->prepare($sqlQuery);
$stmt->execute();
return $stmt;
}
Keep it simple, that will help you in the long run. You only want something in your view that is accessible like an array and PDO offers you that out of the box. You can easily replace it later with - let's say - an array if you need to. Maybe not the best example with the array, but I'm sure you get the idea.
And just a note in the margin: Don't call it model, just call it database or similar. It better names the thingy which is always helpful.

Passing two arrays from controller in cakePHP

I'm trying my best to learn MVC and cakePHP and I had a question about passing arrays to the view. Currently, I have some basic code below.
class AwarenesscampaignsController extends AppController {
public function view($id = null) {
$this->Awarenesscampaign->id = $id;
$this->set('data', $this->Awarenesscampaign->read());
}
This is what I "think" is currently happening.
AwarenesscampaignsController is set up. The view paramater requests id and matches it up with the Model, Awarenesscampaign. This matches up with the database and returns an array which is set to the variable "$data", and then the view is loaded.
My first question: is my understanding accurate?
What I would like to do is with this is to be able to pass another array, from a different model. For instance, I would like to query the table Posts (Controller: PostsController/ Model: Post).
For instance, my first attempt was to do the following inside the function:
$this->Post->find('all');
But this yields the error:
Indirect modification of overloaded property AwarenesscampaignsController::$Post has no effect [APP/Controller/AwarenesscampaignsController.php, line 20]
Additionally, I'm not sure how I would send both variables to the view.
To recap:
Was my understanding accurate?
How do I query a variable from another controller/model?
How do I sent this array to the appropriate view for that controller?
Thanks,
-M
You're on the right lines, and aren't doing it wrong per se. I would say your understanding is pretty good for a beginner.
By default Cake automatically loads a model that it thinks is directly related to the controller. So in AwarenesscampaignController, you can automatically access Awarenesscampaign (the model).
It doesn't know about any other model, though. One way you might solve this is by adding the following property to your controller:
// This has to contain ALL models you intend to use in the controller
public $uses = array('Awarenesscampaign', 'Post');
This goes at the top of the class, before you start declaring the functions. It tells Cake that you want to use other models except the 'default' one, but you have to add that one to the array too, or you'll lose access to it.
You can also use loadModel inside your action, if it's a one-off. It's then accessed the same way as you would access a model normally:
public function view($id = null) {
$this->loadModel('Post');
$posts = $this->Post->find('all');
...
}
To send this to your view, you can call set again, but you might want to change data to something more readable, and to prevent confusion:
public function view($id = null) {
...
$this->set('campaign', $this->Awarenesscampaign->read());
$this->set('posts', $this->Post->find('all'));
}
They'll be accessible as $campaign and $post respectively.
One tweak I would make, though, is to not use 'read' unless you intend to edit something. You can use findByColumnName to get the same data. Since you're using just an id, you can call findById:
$campaign = $this->Awarenesscampaign->findById($id);
There's quite a lot of magic going on there. It just means you can search for a particular value in a more short-hand format.
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
Finally, while you can access other models (as demonstrated), you can't, or generally shouldn't, try and access one controller from another. If you have code that you want to use in more than one controller, but can't go in the model, you can create Components.
http://book.cakephp.org/2.0/en/controllers/components.html#creating-a-component
The manual is fairly comprehensive. While sometimes hard to navigate, it will often have an answer to most of your questions.
http://book.cakephp.org/2.0/en/
1) Your understanding is good enough. What this is doing is basically mapping a row of database table with object. So after setting the Model id $this->Awarenesscampaign->id = $id, now Model is pointing to the row of database table that has id equals to what has been passed to view action.
2) you can query another table by calling the methods of that particular Model. If your model is somehow associated with the current Model that you are in, you can use chaining to call that Model's action. e.g. if your in Posts controller and Post Model is associated with Comment Model t get the data you can chain through.
$comments = $this->Post->Comment->find();
If however your Model of interest is not associated with current Model, there are couple of ways to perform operations of other Model. A good option is to use Class Registry. Say for example you want to use Customer Model which is not related to your current Model. In your controller you will do
$customer= ClassRegistry::init("Customer");
$customers= $customer->find();
3) to set multiple variables for the view you can set them via compact function or using associated row.
$posts = $this->Post->find();
$comments = $this->Post->Comment->find();
$this->set(compact('posts', 'comments'));
// or
$this->set('posts' => $posts, 'comments' => $comments);

Getting database values into objects

The thing is that you have classes and then you have the database data. When you create an object how do you set the objects properties to contain the data in the database ?
I saw something like this and I'm wondering if this is really the best way to do it. I'm sure this is a fairly common issue, but I don't know what are the most accepted solutions on how to handle it.
In this example when the object is created you pass an id as a parameter and then you run a query to the database with the id and you assing the returned values to the object properties. I don't have much PHP experience and haven't seen this used much.
Is this an acceptable way to achieve this purpose ? Is there a better or more accepted way ?
public function __construct($id = null){
if($id != null){
$sql = "SELECT *
FROM users
WHERE user_id = $id";
$res = Db::returnRow($sql);
// $res contains an associative array with database columns and values
if($res){
$this->user_id = $res['user_id'];
$this->user_name = $res['user_name'];
//and so on...
}
}
}
Could somebody provide some sample code or pseudocode to illustrate what is the correct way to do this ?
It could be an acceptable way for a homework maybe. But architecturaly it is not.
Your class that is representing your business data (a user in your example) must be loosely coupled with your database access logic. In the end the PHP class acting as a user should not be aware that the data come from a database, a file or any other resource. Following that you will be able to reuse your user php class in other projects without having to change anything to it! If you have your data access logic inside it you are stuck.
Conclusion: I would suggest to read some resources on Design Pattern (in your situation take a look at DAO pattern) ;) Hint: the one from Head First series is extremely accessible and enjoyable.
You could create a function to do this for you automatically, by looping over the associative array's key/value pairs. Or you could look into using an ORM library.
Yes, you can semi-automate this by having a parent class all objects inherit from. On load, it queries, "SHOW FIELDS FROM [my tablename]" and populates an associative array with the names. If an id has been passed in, it looks for a valid object in that table with that id and assigns the values to the array.
Side note: don't pass your id directly into your query like that. Parametize the sql and wrap a function around any user input to sanitize it.
If it's mysql, you can just do:
$obj = mysql_fetch_object($query);
PDO the ability to use arbitrary classes as the target for a fetch, but beware that they assign the variable data before running the constructor:
$pdo->query($stmt, PDO::FETCH_CLASS, "MyClass", array('foo'=>'bar'));
...where the final parameter contains arguments for your class constructor.

Handling input with the Zend Framework (Post,get,etc)

im re-factoring php on zend code and all the code is full of $_GET["this"] and $_POST["that"]. I have always used the more phpish $this->_request->getPost('this') and $this->_request->getQuery('that') (this one being not so much logical with the getquery insteado of getGet).
So i was wondering if my method was safer/better/easier to mantain. I read in the Zend Framework documentation that you must validate your own input since the request object wont do it.
That leaves me with 2 questions:
What is best of this two? (or if theres another better way)
What is the best practice for validating php input with this methods?
Thanks!
I usually use $this->_request->getParams(); to retrieve either the post or the URL parameters. Then I use the Zend_Filter_Input to do validation and filtering. The getParams() does not do validation.
Using the Zend_Filter_Input you can do application level validation, using the Zend Validators (or you can write your own too). For example, you can make sure the 'months' field is a number:
$data = $this->_request->getParams();
$validators = array(
'month' => 'Digits',
);
$input = new Zend_Filter_Input($filters, $validators, $data);
Extending Brian's answer.
As you noted you can also check out $this->_request->getPost() and $this->_request->getQuery(). If you generalize on getParams(), it's sort of like using the $_REQUEST superglobal and I don't think that's acceptable in terms of security.
Additional to Zend_Filter, you may also use simple PHP to cast the required.
E.g.:
$id = (int) $this->_request->getQuery('id');
For other values, it gets more complicated, so make sure to e.g. quote in your DB queries (Zend_Db, see quoting identifiers, $db->quoteIdentifier()) and in views use $this->escape($var); to escape content.
You can't write a one-size-fits-all validation function for get/post data. As in some cases you require a field to be a integer and in others a date for instance. That's why there is no input validation in the zend framework.
You will have to write the validation code at the place where you need it. You can of course write some helper methods, but you can't expect the getPost() to validate something for you all by itself...
And it isn't even getPost/getQuery's place to validate anything, it's job is to get you the data you wan't, what happens to it from there on should not be it's concern.
$dataGet = $this->getRequest()->getParam('id',null);
$valid = new Zend_Validate_Digits();
if( isset($dataGet) && $valid->isValid($dataGet) ){
// do some...
} else{
// not set
}
I have always used the more phpish $this->_request->getPost('this') and $this->_request->getQuery('that') (this one being not so much logical with the getquery insteado of getGet).
What is best of this two? (or if theres another better way)
Just a quick explanation on the choice of getQuery(). The wording choice comes from what kind of data it is, not how it got there. GET and POST are just request methods, carrying all sorts of information, including, in the case of a POST request, a section known as "post data". A GET request has no such block, any variable data it carries is part of the query string of the url (the part after the ?).
So, while getPost() gets the data from the post data section of a POST request, getQuery() retrieves data from the query string of either a GET or POST request (as well as other HTTP Request methods).
(Note that GET Requests should not be used for anything that might produce a side effect, like altering a DB row)
So, in answer to your first question, use the getPost() and getQuery() methods, this way, you can be sure of where the data source (if you don't care, getParams() also works, but may include additional data).
What is the best practice for validating php input with this methods?
The best place to validate input is where you first use it. That is to say, when you pull it from getParams(), getPost(), or getQuery(). This way, your data is always correct for where you need it, and if you pass it off, you know it is safe. Keep in mind, if you pass it to another Controller (or Controller Action), you should probably check it again there, just to be safe. How you do this depends on your application, but it still needs to be checked.
not directly related to the topic, but
to insure that you get an number in your input, one could also use $var+0
(however if $var is a float it stays a float)
you may use in most cases
$id = $this->_request->getQuery('id')+0;

Categories