Multiple(sharding ) table in a Model CakePHP - php

There are many tables as the following:
table_2010
table_2009
table_2008
table_2007
.
.
Using MySQL 4 + PHP5 + CakePHP 1.3
My Question is
How to treat these tables in a model?
I wanna treat like this
Table->find('all',"2010",array("conditions"=>""));

I agree with Nik -- unless you're sharding for performance reasons, I would combine all of your tables into one table, with a column for the year (if you make it an INT, it won't affect performance much).
However, if you need to shard your tables, I'd recommend that you just override the Model::find() method to accept additional parameters. In your model, write something like the pseudocode below:
function find( $type, $options = array() ) {
if( isset( $options['table'] ) ) { // this is the index where you'll pass your table name
$this->setSource( $options['table'];
}
return parent::find( $type, $options );
}
Basically the call to setSource will change your table that you are querying, at runtime. See Can a CakePHP model change its table without being re-instantiated? for more information.

For me the smarter way is to use one table - posts for example and in that table to have a special column called year. So the find will be something like:
$this->Post->find('all', array('year'=>2010));

Related

batch delete in codeigniter

insert_batch() is a codeigniter build-in function which inserts 100 data of rows at a time. That's why it is so much faster for inserting large amount of data.
Now I want to delete large number of data like insert_batch() function does.
Is their any way to do it?
Already I am using where_in function but it is not that much faster like insert_batch() and that's why timeout error occur often.
I want to know specially that can i make a function like insert_batch() or insert_update() in codeigniter system/database/db_query_builder ?
If I can how to do it. or any other suggestion please ?
If we are talking about alot of rows, it may be easier to perform a table 'switch' by inserting and dropping the the original table.
However one drawback to this will mean any Auto Increment IDs will be lost.
<?php
// Create a Copy of the Same Data structure
$this->db->query( "CREATE TABLE IF NOT EXISTS `TB_TMP` .. " );
// Select the data you wish to -Keep- by excluding the rows by some condition.
// E.g. Something that is no longer active.
$recordsToKeep =
$this->db
->select( "*" )
->get_where( "TB_LIVETABLE", [ "ACTIVE" => 1 ])
->result();
// Insert the Excluded Rows.
$this->db->insert_batch( "TB_TMP", $recordsToKeep );
// Perform the Switch
$this->db->query( "RENAME TABLE TB_LIVETABLE TO TB_OLDTABLE" );
$this->db->query( "RENAME TABLE TB_TMP TO TB_LIVETABLE " );
$this->db->query( "DROP TABLE TB_OLDTABLE" );

CONCAT columns with Laravel 5 eloquent

Consider me as laravel beginner
The goal is: I have two colums, now I need the id to be prefixed with the component name of same row in the table.
For Example (Working)... I have Mysql like
SELECT CONCAT(components.name," ", components.id) AS ID
FROM `components`
And output is
ID
|TestComp 40 |
-------------
|component 41 |
-------------
|test 42 |
I need the same in laravel eloquent way, as here Component is Model name. So i tried something like
$comp=Component::select("CONCAT('name','id') AS ID")->get()
but it doesn't work.
I think because the syntax is wrong.
Kindly help me with the correct syntax. Using laravel Models.
Note: I made the above query, referring this as which available on internet.
User::select(DB::raw('CONCAT(last_name, first_name) AS full_name'))
You need to wrap your query in DB::raw:
$comp = Component::select(DB::raw("CONCAT('name','id') AS ID"))->get()
Also, note because you are doing your query like this, your model might behave differently, because this select removes all other fields from the select statement.
So you can't read the other fields from your model without a new query. So ONLY use this for READING data and not MODIFYING data.
Also, to make it in a nice list, I suggest you modify your query to:
$comp = Component::select(DB::raw("CONCAT('name','id') AS display_name"),'id')->get()->pluck('display_name','id');
// dump output to see how it looks.
dd($comp);// array key should be the arrray index, the value the concatted value.
I came to this post for answers myself. The only problem for me is that the answer didn't really work for my situation. I have numerous table relationships setup and I needed one of the child objects to have a concatenated field. The DB::raw solution was too messy for me. I kept searching and found the answer I needed and feel it's an easier solution.
Instead of DB::raw, I would suggest trying an Eloquent Accessor. Accessors allow you to retrieve model attributes AND to create new ones that are not created by the original model.
For instance, let's say I have a basic USER_PROFILE table. It contains id, first_name, last_name. I have the need to CONCAT the two name attributes to return their user's full name. In the USER_PROFILE Model I created php artisan make:model UserProfile, I would place the following:
class UserProfile extends Model
{
/**
* Get the user's full concatenated name.
* -- Must postfix the word 'Attribute' to the function name
*
* #return string
*/
public function getFullnameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
}
From here, when I make any eloquent calls, I now have access to that additional attribute accessor.
| id | first_name | last_name |
-------------------------------
| 1 | John | Doe |
$user = App\UserProfile::first();
$user->first_name; /** John **/
$user->fullname; /** John Doe **/
I will say that I did run into one issue though. That was trying to create a modified attribute with the same name, like in your example (id, ID). I can modify the id value itself, but because I declared the same name, it appears to only allow access to that field value and no other field.
Others have said they can do it, but I was unable to solve this questions EXACT problem.
I working on posgresql and mysql:
DB::raw('CONCAT(member.last_name, \' \', member.first_name) as full_name')
$text = "other";
$limit = 100
public function get_data($text, $limit)
{
$result = $this->select('titulo', 'codigo', DB::Raw("CONCAT(codigo, ' ', titulo_long) AS text_search"))
->where('tipo', '=', 2)
->having('text_search', 'LIKE', "%$text%")
->limit($limit)
->get();
return $result;
}
}
Here is the example of columns concatenation in Laravel.
I need to search the user by name and I have three columns for the user name (name_first, name_middle, name_last), so I have created a scope in Laravel UserModel which takes $query and user name as the second parameter.
public function scopeFindUserByName($query,$name) {
// Concat the name columns and then apply search query on full name
$query->where(DB::raw(
// REPLACE will remove the double white space with single (As defined)
"REPLACE(
/* CONCAT will concat the columns with defined separator */
CONCAT(
/* COALESCE operator will handle NUll values as defined value. */
COALESCE(name_first,''),' ',
COALESCE(name_middle,''),' ',
COALESCE(name_last,'')
),
' ',' ')"
),
'like', '%' . $name . '%');
}
and you can use this scope anywhere you need to search user by his name, like
UserModel::findUserByName("Max Begueny");
OR
$query = UserModel::query();
$query->findUserByName("Max Begueny");
To check the result of this SQL query just go through from this post.
https://stackoverflow.com/a/62296860/11834856
this code should work:
User::select(\DB::raw('CONCAT(last_name, first_name) AS full_name)')
Scrubbing the Data: Before using Laravel and now, when developing in other languages, I would use CONCAT() on a regular basis. The answers here work to a degree but there still isn't an elegant way to use CONCAT() in Laravel/Eloquent/Query Builder
that I have found.
However, I have found that concatenating the cols AFTER returning the results works well for me and is usually very fast - Scrubbing the data - ( unless you have a huge result which should probably be "chunked" anyway for performance purposes ).
foreach($resultsArray AS $row){
$row['fullname'] = trim($row['firstname']).' '.trim($row['lastname']);
}
This is a tradeoff of course but, personally, I find it to be much more manageable and doesn't limit my use of Eloquent as intended as well as the Query Builder. ( the above is pseudo code - not tested so tweak as needed )
There are other workarounds as well that don't mess with Eloquent/Query Builder functionality such as creating a concatenated col in the table, in this case full_name - save the full name when the record is inserted/updated. This is not uncommon.

mysql | PHP | Join within own table

i dont know if i am doing right or wrong, please dont judge me...
what i am trying to do is that if a record belongs to parent then it will have parent id assosiated with it.. let me show you my table schema below.
i have two columns
ItemCategoryID &
ItemParentCategoryID
Let Suppose a record on ItemCategoryID =4 belongs to ItemCategoryID =2 then the column ItemParentCategoryID on ID 4 will have the ID of ItemCategoryID.
I mean a loop with in its own table..
but problem is how to run the select query :P
I mean show all the parents and childs respective to their parents..
This is often a lazy design choise. Ideally you want a table for these relations or/and a set number of depths. If a parent_id's parent can have it's own parent_id, this means a potential infinite depth.
MySQL isn't a big fan of infinite nesting depths. But php don't mind. Either run multiple queryies in a loop such as Nil'z's1, or consider fetching all rows and sorting them out in arrays in php. Last solution is nice if you pretty much always get all rows, thus making MySQL filtering obsolete.
Lastly, consider if you could have a more ideal approach to this in your database structure. Don't be afraid to use more than one table for this.
This can be a strong performance thief in the future. An uncontrollable amount of mysql queries each time the page loads can easily get out of hands.
Try this:
function all_categories(){
$data = array();
$first = $this->db->select('itemParentCategoryId')->group_by('itemParentCategoryId')->get('table')->result_array();
if( isset( $first ) && is_array( $first ) && count( $first ) > 0 ){
foreach( $first as $key => $each ){
$second = $this->db->select('itemCategoryId, categoryName')->where_in('itemParentCategoryId', $each['itemParentCategoryId'])->get('table')->result_array();
$data[$key]['itemParentCategoryId'] = $each['itemParentCategoryId'];
$data[$key]['subs'] = $second;
}
}
print_r( $data );
}
I don't think you want/can to do this in your query since you can nest a long way.
You should make a getChilds function that calls itself when you retrieve a category. This way you can nest more than 2 levels.
function getCategory()
{
// Retrieve the category
// Get childs
$childs = $this->getCategoryByParent($categoryId);
}
function getCategorysByParent($parentId)
{
// Get category
// Get childs again.
}
MySQL does not support recursive queries. It is possible to emulate recursive queries through recursive calls to a stored procedure, but this is hackish and sub-optimal.
There are other ways to organise your data, these structures allow very efficient querying.
This question comes up so often I can't even be bothered to complain about your inability to use Google or SO search, or to offer a wordy explanation.
Here - use this library I made: http://codebyjeff.com/blog/2012/10/nested-data-with-mahana-hierarchy-library so you don't bring down your database

Help setting up logic for advanced search parameters in PHP

I would like to create an advanced search form much like a job site would have one that would include criteria such as keyword, job type, min pay, max pay, category,sub category etc...
My problem is deciding on how best to set this up so if I have to add categories to the parameters I'm not having to modify a whole bunch of queries and functions etc...
My best guess would be to create some sort of associative array out of all of the potential parameters and reuse this array but for some reason I feel like it's a lot more complex than this. I am using CodeIgniter as an MVC framework if that makes any difference.
Does anybody have a suggestion as how best to set this up?
Keep in mind I will need to be generating links such as index.php?keyword=designer&job_type=2&min_pay=20&max_pay=30
I hope my question is not to vague.
I don't know if it's what you need, but I usually create some search class.
<?php
$search = new Search('people');
$search->minPay(1000);
$search->maxPay(4000);
$search->jobType('IT');
$results = $search->execute();
foreach ($results as $result)
{
//whatever you want
}
?>
You can have all this methods, or have some mapping at __set() between method name and database field. The parameter passed to the constructor is the table where to do the main query. On the methods or mapping in the __set(), you have to take care of any needed join and the fields to join on.
There are much more 'enterprise-level' ways of doing this, but for a small site this should be OK. There are lots more ActiveRecord methods you can use as necessary. CI will chain them for you to make an efficient SQL request.
if($this->input->get('min_pay')) {
$this->db->where('min_pay <', $this->input->get('min_pay'));
}
if($this->input->get('keyword')) {
$this->db->like($this->input->get('keyword'));
}
$query = $this->db->get('table_name');
foreach ($query->result() as $row) {
echo $row->title;
}
To use Search criterias in a nice way you should use Classes and Interfaces.
Let's say for example you define a ICriteria interface. Then you have different subtypes (implementations) of Criteria, TimeCriteria, DateCriteria, listCriteria, TextSearch Criteria, IntRange Criteria, etc.
What your Criteria Interface should provide is some getter and setter for each criteria, you'll have to handle 3 usages for each criteria:
how to show them
how to fill the query with the results
how to save them (in session or database) for later usage
When showing a criteria you will need:
a label
a list of available operators (in, not in, =, >, >=, <, <=, contains, does not contains) -- and each subtypes can decide which part of this list is implemented
an entry zone (a list, a text input, a date input, etc)
Your main code will only handle ICriteria elements, ask them to build themselves, show them, give them user inputs, ask them to be saved or loop on them to add SQL criteria on a sql query based on their current values.
Some of the Criteria implementations will inherits others, some will only have to define the list of operators available, some will extends simple behaviors to add rich UI (let's say that some Date elements should provide a list like 'in the last day', 'in the last week', 'in the last year', 'custom range').
It can be a very good idea to handle the SQL query as an object and not only a string, the way Zend_Db_Select works for example. As each Criteria will add his part on the final query, and some of them could be adding leftJoins or complex query parts.
Search queries can be a pain sometimes, but not as big of a pain as pagination. Luckily, CodeIgniter helps you out a bit with this with their pagination library.
I think you're on the right track. The basic gist, I would say, is:
Grab your GET variables from the URL.
Create your database query (sanitize the GET values).
Generate the results set.
Do pagination.
Now, CodeIgniter destroys the GET variable by default, so make sure you enable http query strings in your config file.
Good luck!
I don't know anything about CodeIgniter, but for the search application I used to support, we had drop-down combo-boxes with category options stored in a database table and would rely on application and database cacheing to avoid round-trips each time the page was displayed (an opportunity for learning in itself ;-). When you update the table of job_type, location, etc. the new values will be displayed in your combo-box.
It depends on
how many categories you intend to have drop-down lists
how often you anticipate having to update the list
how dynamic you need it to be.
And the size of your web-site and overall activity are factors you will have to consider.
I hope this helps.
P.S. as you appear to be a new user, if you get an answer that helps you please remember to mark it as accepted, or give it a + (or -) as a useful answer
A pagination class is a good foundation. Begin by collecting query string variables.
<?php
// ...in Pagination class
$acceptableVars = array('page', 'delete', 'edit', 'sessionId', 'next', 'etc.');
foreach($_GET as $key => $value) {
if(in_array($key, $acceptableVar)) {
$queryStringVars[] = $key . '=' . $value;
}
}
$queryString = '?' . implode('&', $queryStringVars);
$this->nextLink = $_SEVER['filename'] . $queryString;
?>
Duplicate the searchable information into another table. Convert sets of data into columns having two values only like : a search for color=white OR red can become a search on 10 columns in a table each containing one color with value 1 or 0. The results can be grouped after so you get counters for each search filter.
Convert texts to full text searches and use MATCH and many indexes on this search table. Eventually combine text columns into one searchable column. The results of a seach will be IDs which you can then convert into the records with IN() condition in SQL
Agile Toolkit allows to add filters in the following way (just to do a side-by-side comparison with CodeIgniter, perhaps you can take some concepts over):
$g=$this->add('Grid');
$g->addColumn('text','name');
$g->addColumn('text','surname');
$g->setSource('user');
$conditions=array_intersect($_GET, array_flip(
array('keyword','job_type','min_pay'));
$g->dq->where($conditions);
$g->dq is a dynamic query, where() escapes values passed from the $_GET, so it's safe to use. The rest, pagination, column display, connectivity with MVC is up to the framework.
function maybeQuote($v){
return is_numeric($v) ?: "'$v'";
}
function makePair($kv){
+-- 7 lines: $a = explode('=', $kv);
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
}
function makeSql($get_string, $table){
+-- 10 lines: $data = explode('&', $get_string);
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
}
$test = 'lloyd=alive&age=40&weather=hot';
$table = 'foo';
print_r(makeSql($test, $table));

Joining Customer on Attribute

I'm trying to filter my orders which are returned back by the magento API by a customer attribute. I tried several approaches but nothing seem to work.
I'm using Magento 1.4.1.1 atm and the api does this at the moment:
$billingAliasName = 'billing_o_a';
$shippingAliasName = 'shipping_o_a';
$collection = Mage::getModel("sales/order")->getCollection()
->addAttributeToSelect('*')
->addAddressFields()
->addExpressionFieldToSelect(
'billing_firstname', "{{billing_firstname}}", array('billing_firstname'=>"$billingAliasName.firstname")
)
->addExpressionFieldToSelect(
'billing_lastname', "{{billing_lastname}}", array('billing_lastname'=>"$billingAliasName.lastname")
)
->addExpressionFieldToSelect(
'shipping_firstname', "{{shipping_firstname}}", array('shipping_firstname'=>"$shippingAliasName.firstname")
)
->addExpressionFieldToSelect(
'shipping_lastname', "{{shipping_lastname}}", array('shipping_lastname'=>"$shippingAliasName.lastname")
)
->addExpressionFieldToSelect(
'billing_name',
"CONCAT({{billing_firstname}}, ' ', {{billing_lastname}})",
array('billing_firstname'=>"$billingAliasName.firstname", 'billing_lastname'=>"$billingAliasName.lastname")
)
->addExpressionFieldToSelect(
'shipping_name',
'CONCAT({{shipping_firstname}}, " ", {{shipping_lastname}})',
array('shipping_firstname'=>"$shippingAliasName.firstname", 'shipping_lastname'=>"$shippingAliasName.lastname")
);
Which is the default API call I guess. Now I just want to join a customer attribute called update - how do I achieve this simple task?
Or is this not possible on a flat table like sales_flat_order?
Whenever I need to do this I use something like:
Joining An EAV Table (With Attributes) To A Flat Table
It's not well optimised but you should be able to pick out the parts you need.
PS.
I think I'll explain what I mean by optimised since it's important. In the heart of the method is this bit:
->joinLeft(array($alias => $table),
'main_table.'.$mainTableForeignKey.' = '.$alias.'.entity_id and '.$alias.'.attribute_id = '.$attribute->getAttributeId(),
array($attribute->getAttributeCode() => $field)
);
If you know MySQL then you'll know it will only pick one index when joining a table, the more specific the better. In this case only the entity_id and attribute_id fields are being used so MySQL is restricted to those. Both columns are indexed but the cardinality is low.
If the condition also included the entity type then MySQL would have the choice of using IDX_BASE which indexes the columns entity_type_id,entity_id,attribute_id,store_id in that order (it needs to process them left to right). So something like this results in a much improved EAV performance - depending on how many rows on the 'left' table it could be several hundred- or thousand-fold better.
$alias.'.entity_type_id='.$entityType->getId().' AND main_table.'.$mainTableForeignKey.' = '.$alias.'.entity_id AND '.$alias.'.attribute_id = '.$attribute->getAttributeId().' AND '.$alias.'.store_id=0'

Categories