Is there a codeigniter plugin that allows me to quickly create find by functions without writing code on every field in a db table?
I find myself writing a lot of functions for tables such as findbyid findbyfirstname findbyemail and so on, any libraries already written to speedup my dev time? i tried googling but i havent come across any.
If you mean you have to write multiple methods in your model to find rows in a table by a specific field, you could just pass an associative array containing the fields and values you want to search to a generic function - something like:
function search_mytable($search=array()) {
$this->db->select('mytable.*');
$this->db->from('mytable');
if(!empty($search)
$this->db->where($search);
}
There's more information about what you can pass the CI active record where method here http://codeigniter.com/user_guide/database/active_record.html#select
If it's just simple data retrieval, you can just do something like this:
function find($column, $value)
{
$this->db->where($column, $value);
//etc
}
for simple queries. As BrynJ suggests, the Active Record class is rather flexible when it comes to taking parameters.
Related
I am using CodeIgniter but this question applies in a general sense too.
I have a table of transactions with columns
item_name | type | date | price | document
I want to do the following in two completely independent cases.
1) Get a list of transactions within a certain date range.
2) Get the total price of each transaction.type within a certain date range.
The former can be achieved by simply using a select statement with > datetimestamp
The latter can be achieved by selecting the SUM, and grouping by the type whilst like implementing any required where conditionals e.g with > datetimestamp
Although a simple case, to achieve this I need to have two methods however the bulk of both of these methods (namely the WHERE clauses) are duplicated across both methods.
In terms of speed etc it does not matter but it seems like pointless code reproduction.
A second example is as follows.
I previously had a method get_data($ID) which would get a row from a table based on the ID passed in.
As such in a separate method I would get my 100 items for example.. return an array, loop through them and call get_data for each.
This setup meant that many different methods could get different lists from different sources and then still use the same get_data function and a loop to get the required data.
This minimized code duplication but was incredibly ineffiecient as it meant looping through loads of data items and hundreds of db queries.
In my current setup i just join the data table in each of my methods - code duplication but clear improved efficiency.
A final example is as follows
In codeigniter I can have a function such as the following:
get_thing($ID)
{
$this->load->database();
$this->db->where('ID',$ID);
$this->db->get('table');
}
BUT in alternate situations i might want to only get items in a specific folder.. as such making the function more generic works better.. e.g.
get_thing($array)
{
$this->load->database();
$this->db->where($array);
$this->db->get('table');
}
but then I might want to use this function in two different contexts e.g a user page and an admin page whereby admins can see all items, even unverified ones. My code now becomes:
get_thing($array,$show_unverified = false)
{
$this->load->database();
$this->db->where($array);
if($show_unverified == false)
{
$this->db->where('verified','YES');
}
$this->db->get('table');
}
As you can probably see this can quickly get out of hand and methods can become overly complex, confusing and full of conditionals.
My question is as follows - What are best practices for minimizing code duplication, and how could they be applied to the above situations? I spent hours and hours trying to make my code more efficient yet I'm getting nowhere because I cant workout what I should really be trying to achieve.
Cheers
My idea on code duplication in database access functions is that it is often better to keep it separate.
My rule here is especially that a function should not return different kinds of data depending on the parameter, for example it should not return a single user sometimes and other times an array of users. It may return error codes (false) though.
It is ok though if the function implements different access levels, which are shared across several pages.
This basically always comes back to common sense. You should try to minimize duplicate code and try to reduce complexity within single function. Keep them small and simple.
So basically everytime you try to generalize a function like this you would have to ask if the problem of duplicate code is bigger than the problem of overly complex functions.
In this case i would stop at your second point and next you could create some wrappers for the most common tasks (but be carefull not to make a maze of wrappers)
//you generic function
function get_thing($array)
{
$this->load->database();
$this->db->where($array);
$this->db->get('table');
}
// a nice and friendly wrapper
function get_thing_by_id($id)
{
get_thing(array('id' => $id));
}
// this is just getting silly. don't go crazy with wrappers, only for very often used things.
// and yes the function name is purposely crazy ;)
function get_thing_verified_by_name_and_city_and_some_more($name, $city, $somethingElse)
{
get_thing(array('name' => $name, 'city' => $city, 'somethingelse' => $somethingElse));
}
This answers the first part of your question. Assuming you're using mysql_fetch_assoc or similar. As you're iterating over the result sets you could store count values in a variable in the loop for the total price of each transaction type.
The second part as long as you're not repeating code ad infinitum which would cause you issues down the line when maintaining the code base, it's OK. For your function you could always test the type of variable being passed to the function and set conditional behaviours accordingly.
Have a look at the factory pattern or strategy pattern relating to software design patterns for further insight.
I have an issue I keep hitting the wall on and I need some suggestions on the best way around this.
I got a loop that grabs data from my model, but within that loop, I have to validate it against a library, but I cant seem to find a good way to do that.
for example, I got a model method that looks similar to this:
public function GetUserList() {
$this->db->select('username, email, joindate, rank')->from('user');
$query = $this->db->get();
//loop through data and bind to an array.
foreach ($query->result() as $row) {
$users[] = $row;
}
return $users;
}
when the page loads the user will see a list of users and their info, but some users don't want their email address to be shown, we then have to use the user library to fetch the settings for that user and see if we should hide that email or not.
the only solution i found was to use a helper function as a detour, but I feel that is going to bite me in the future duplicating things like that.
the other issue I found was once you created the library it didn't restart the object as a new one (like it would in vanilla PHP).
I'm hoping someone has a good way to resolve a setback like this.
One last item, if it makes a difference, I'm also using Twig to handle my views.
You do not need to load every library file using the load() function. The load() function is for loading a single instance in the global (CI) namespace. If you want to work with a collection of objects, you should require the file like you normally would in PHP.
I would personally join on your preferences table if possible. That way you wouldn't have to do a separate query for every single user.
I have a question regarding performance when using OOP in PHP together with databases. I'll ask my question by example, suppose a class foo represents a row from some table. Now suppose I need to use foo at 5 different pages on my web app.
The catch is that on each of the 5 pages I will use data from different columns. (i.e. the first page will use column1 and column2 while the second page uses column3 and column 4, etc..)
The OOP approach (as far as I can see) would suggest that when I initialize foo on some particular row I would connect and fetch all the columns of that row and build my object. I could then proceed with my logic and use whatever data that I might need.
The issue I have with this is that with the procedural approach (which I'm more used to when it comes to web) would not waste resources to download columns that I do not need since the query would be specifically tailored to the needs of the particular page.(i.e. If im on the first page I would only download column1 and column2 since that's what I need.)
Am i going about the OOP approach wrong or is the extra overhead so insignificant that developers in general download data which they do not need?
Thanks and sorry if this has already been covered, I thought it would be an interesting topic! :)
Erik
further clarification:
The class is like:
class foo
{
$column1;
$column2;
$column3;
$column4;
public function _construct($id)
{
//get column 1,2,3 and 4 from database where table_id = $id
}
}
The issue is that if i only need column1 one one page i download column2,3 and 4 for nothing. In procedural approach you would not do that. Is my OOP model bad or is this ok?
You can still incorporate the selective query inside of an OOP class by using either an array of columns to grab upon construction, or by using a public class method to handle the query grabbing.
Example of constructor:
<?php
class Foo{
public function __construct( $column ) {
if(is_array($column)){
if(count($column) > 1){
$result = mysql_query('SELECT `'.implode('`,`', $column).'` FROM `table`;');
}else{
$result = mysql_query('SELECT `'.$column[0].'` FROM `table`;');
}
}else{
$result = mysql_query('SELECT `'.$column.'` FROM `table`;');
}
$this->result = mysql_result($result, 0);
}
}
?>
The public function method would be identical to that, except you could return the result instead of setting $this->result.
I'm not entirely sure I understand your question. There are three things that I think could apply to how you are approaching this problem:
A) You are trying to build an object and then use data contained in that object throughout your script.
B) You are using a PDO style database pull.
C) You are using PHPs SPL to produce an iteration over an object which contains methods to pull information from the database.
I'll assume for now that you are using option A. Please forgive me if I am wrong and I am not trying to underestimate your knowledge at all...just getting it started here.
The approach of OOP is not to pull in all data to have it available throughout your script. Think of it as a collection of functions instead of a collection of data, although it could easily be either or both. You'll write your class methods just like you write functions without OOP. The only difference is, the object can be used to communicate with your script over the number of times that you need it to...
To answer your question plainly, I never pull more data than I need. For both security and performance reasons. You should use a class just like you use the procedural style. You could do all of your data pulls that will be required for the script upon instantiating the class (using a constructor method), but make sure that it's only the data you will need.
----Added
class foo{
function getData($page){
//Query to get the results you want based on the page number entered...
//Process it as you normally would into a result set, array, or whatever.
return $results;
}
}
Then call that
$foo = new Foo();
$page = "The page or the column that you want to pull";
$data = $foo->getData($page);
Your still doing everything procedurally, but now you have a dynamic function that can pull data based on what you send in as page... IN this case, I don't see any reason to use a constructor...only a getter method.
Does that help?
The general approach will be to select only the columns you need
foo->db->tablename->select('all', where date = $date).
Take a quick look at frameworks such as cakephp and symfony, it might help you get a better idea of how it's generally done.
My two cents. It depends on a number of things and how it affects the application as a whole ie. # of database requests, size per record, size of rowset, etc
I personally load all columns and profile to look for bottlenecks when I experience slow requests or high memory usage. If I have bottlenecks then I consider lazy loading only required columns at that point.
I'm currently writing an EPOS integration for Magento. When an order is made, it's ID is placed in a queuefile. Once a minute, a cron looks at the queue, fires the top order to the EPOS web api and then moves the ID to either a successlist file or a faillist file, depending on the outcome.
In order to display the contents of these lists to the user, I created an admin page that reads the file (containing a serialized array), creates a varien_object containing the order ID, customer name and timestamp for each order, and then stores all of these in an instance of a Varien_Data_collection. This collection is then passed to the _prepareCollection function in the grid.php for rendering the grid view.
In 1.4.1.1, the grid renders fine, but the pagination is broken and the filtering doesn't work.
In 1.3.2.4 the grid renders but says there are 'No Records Found'.
Does anybody know what could be causing these issues, and if there is a better way of going about displaying information from a file in Magento?
The reason why you can see the entries (1.4+), but can't filter is that Magento is using the collection api to modify the object. If you are just pulling values out of a model, its no big deal, but if you are searching and filtering, Magento needs the collection to be an instance of a database. It uses Varien_Db_Select objects to make queries which resolve to raw sql, so that's not going to work on an array.
I would recommend trying to deal with the data in a different way.
It sounds like you are working with a flat file, so the obvious solution of constructing a sql query to fetch everything for you won't cut it. I would try creating an instance of Zend_Db_Table, and populating the values on the fly.
Something like this:
class Foo_Bar_Model_Resource_Success_Collection extends Mage_Core_Model_Resource_Db_Abstract
{
public function _construct()
{
//declare write adapter in config
$table = new Zend_Db_Table('my_db.my_table');
foreach($this->getEposArray() as $entry)
$table->insert($entry);
$this->_init('my_table', 'id');
}
}
Admittedly, I've never done anything quite like this, but have had the custom grid filter problem crop up on me before, and know that if you want to search, you need to have your data in a table of some sort. Check out Zend's docs on the matter. I'm pretty sure that there's a way to do this inside of Magento, but I couldn't begin to think about a solution.
My advice, store your cron job data in a database, it will make pulling the data back out much easier.
I've been developing a site over the past few weeks using CodeIgniter as the framework. I've been thinking of the best way to accomplish something, which in a lot of other frameworks in other languages is relatively simple: sortable tables. CodeIgniter switches off query strings by default, because your URLs contain method parameters. So a URL might look like:
/controller/method/param1/param2
You might think that you could just add in sortBy and sortOrder as two additional parameters to the controller method. I don't particularly want to do that, mainly because I want to have a re-usable controller. When you use query string parameters, PHP can easily tell you whether there is a parameter called sortBy. However, when you're using URL based parameters, it will vary with each controller.
I was wondering what my options were. As far as I can see they are something like:
Pass in my sortBy and sortOrder parameters, just suck it up, and develop some less-than-reusable component for it.
Have an additional controller, which will store the sortBy and sortOrder in the session (although it would have to know where you came from, and send you back to the original page).
Have some kind of AJAX function, which would call the controller above; then reload the page.
Hack CodeIgniter to turn query strings back on. Actually, if this is the only option, any links to how to do this would be appreciated.
I just can't quite believe such a simple task would present such a problem! Am I missing something? Does anyone have any recommendations?
While I love jQuery, and I'm already using it on the site, so TableSorter is a good option. However, I would like to do server-side sorting as there are some pages with potentially large numbers of results, including pagination.
If you're OK with sorting on the client side, the Tablesorter plugin for jQuery is pretty nice.
I ran into this with a fairly complex table. The hard part was that the table could grow/shrink depending on certain variables!! Big pain :(
Here's how I handled it..
Adjusted system/application/config/config.php to allow the comma character in the URI:
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-,';
Adjust my controller with a sorting function:
function sorter() {
//get the sort params
$sort = explode(",",$this->uri->segment(3)); //the 3rd segment is the column/order
//pass the params to the model
$data = $this->model_name->get_the_data($sort[0],$sort[1]);
$this->_show($data);
}
function _show($data) {
//all the code for displaying your table
}
I've oversimplified, but you get the idea. The purpose is to have a url like this:
/controller/sorter/columnname,sortorder
The sorter function calls another internal function to deal with the display/template/view logic - it's job is to deal with the sorting call and get the appropriate data from the model.
Of course, this could be reduced to just your current function:
function showGrid() {
$sort = $this->uri->segment(3);
if ($sort) {
//get the data sorted
} else {
//get the data the default way
}
//rest of your view logic
}
That way, you don't even need a separate function - and can use the third segment to define your sorting.
I recently added this Table sorter (which uses Prototype) to a bunch of my pages. It's fast and pretty easy to implement.