CakePHP models findFirst method issue - php

I'm not familiar with CakePHP too close. Recently I've ran into problem using Model. I need to get exaclty one row from database, modify some columns values and save it back. Pretty simple, right?
What do I try to do:
$condition = array('some_id_column' => $another_models_id);
$model = $this->MyModel->findFirst($condition);
BUT i get FALSE in $model variable. At hte same time
$condition = array('some_id_column' => $another_models_id);
$model = $this->MyModel->findAll($condition);
returns array. Its structure is something like:
array (
0 =>
array (
'MyModel' =>
array (
'id' => '1',
'some_id_column' => '123456',
'some_field' => 'some text',
...
),
),
I'd go with findAll if it did not return an array of arrays, but array of models (in my case - of one model). What do I want to achieve:
$condition = array('some_id_column' => $another_models_id);
$model = $this->MyModel->findFirst($condition);
$model->some_field = 'some another text';
$model->save();
Could you help me out to understand how it's usually done in CakePHP?
I'd also like to hear why findAll finds row and findFirst fails to find it... It just does not make sense to me... They should work in almost the same way and use the same database APIs...
If I can not do what I want in CakePHP, would you write a receipt how it is usually done there ?

There is no such method as findFirst.
You're probably looking for find('first', array('conditions' => array(...))).

Related

Is there a cleaner way to send an array from a controller to a function, model or view?

Is there a more compact way to send an array to a function?
Here's what I'm currently doing:
$data = array(
'id' => '1'
);
$result = $this->Tests_model->DoSomething($data);
What I'd like to do is just:
$result = $this->Tests_model->DoSomething(array('id' => '1'));
or
$result = $this->Tests_model->DoSomething(('id' => '1'));
...but I can't seem to format the data inside the ( and ). I still want to pass an array, for better code-reuse. Is there a way to do this?
This line
$result = $this->Tests_model->DoSomething(array('id' => '1'));
is completely valid and can be used.
You need to change the third example to make it work. What you're looking for is
$result = $this->Tests_model->DoSomething(['id' => '1']);
The syntax ['id' => '1'] is just another way of writing array('id' => '1') they produce the exact some thing.
One last way to create an array and add a key/value pair to it is like this.
$data['id'] = '1'; //assigns a value of '1' to the key 'id' in an array named $data.
The above is just alternate syntax for writing.
$data = array('id' => '1');
None of these variations on syntax is better or worse other than in terms of "readability" which is often a matter of opinion.
Everything you could want to know about arrays in the PHP Documentation.

CakePHP overriding identical "field" in search conditions

I've come across an odd problem using CakePHP 1.3 to find information. Let's use this dbschema as an example:
id is int(11) PRIMARY auto_increment
amount is float(10,2) NULL
status is ENUM(Completed, Removed, Pending)
id amount status
1 100.00 Completed
2 100.00 Removed
3 100.00 Completed
4 100.00 Completed
5 100.00 Pending
When using Cake's find to retrieve data from this table, I use this query:
$this->Testtable->find('all', array(
'conditions' => array(
'status LIKE ' => 'Removed',
'status LIKE ' => 'Pending',
'status LIKE ' => 'Completed'
)
))
Looking at this query, I would assume that Cake would return all rows that match all of those conditions (which is totally acceptable in SQL), however it only uses the last condition and returns WHERE status LIKE 'Completed'.
I ran a test doing this, which returned all rows correctly (I know it's a more "correct" way to do the query anyway):
'conditions' => array(
'status' => array('Removed', 'Pending', 'Completed')
)
Take the opposite for an example, I want to return all rows that aren't Removed or Pending:
'conditions' => array(
'status !=' => 'Removed',
'status !=' => 'Pending'
)
This query returns all rows with Completed and Removed, as it only listens to the last statement. I assume that this is happening because instead of concatenating these search conditions into the query, Cake is overwriting the conditions based on the "field" being status !=. I can prove this theory by adding a space after the != in either of those conditions, creating the desired result of only Confirmed records.
Can anybody tell me why Cake would do this? As this is a legitimate thing to do in SQL, I see no reason that Cake wouldn't allow you to do it. Does anybody know if this issue is fixed up in newer versions of Cake?
I suppose that this comes down to the fact that at the end of the day, I am reassigning the array value based on that key, and it's not actually CakePHP's fault at all. I had a look into Cake's model.php and found this:
$query = array_merge(compact('conditions', 'fields', 'order', 'recursive'), array('limit' => 1));
I ran a test:
$array = array(
'conditions' => array(
'test' => 'yes',
'test' => 'no'
)
);
$var = 'hello';
$c = compact('array', 'var');
print_r($c);
As mentioned above, compact is only receiving the value no from the test key. I assumed that the use of compact to merge variables/arrays into the query would recursively merge similar keys from the conditions array into the last specified, but it turns out that yes isn't even making it that far as it's being redefined on the spot.
I suppose this is a limitation of PHP rather than Cake, but it is still something that didn't occur to me and should be done differently somehow (if it hasn't already) in future.
Edit
I ran a couple more tests. I wrapped identical conditions in their own arrays, then compared them the way Cake's find functions would. Using compact (which Cake does) the arrays containing identical keys remain intact, however using array_merge, the first key is overwritten by the second. I guess in this case it's a very, very good thing that Cake uses compact instead of array_merge to merge its query criteria.
$array = array(
array('test' => 'yes'),
array('test' => 'no')
);
$m = array_merge($array[0], $array[1]);
$c = compact('array');
print_r($c);
print_r($m);
Result:
Array
(
[array] => Array
(
[0] => Array
(
[test] => yes
)
[1] => Array
(
[test] => no
)
)
)
Array
(
[test] => no
)
While this is obviously a simple problem in the way you fundamentally write PHP code, it wasn't inherently obvious while writing in Cake syntax that conditions would overwrite each other...
Basic PHP: Don't use the same array key twice
'conditions' => array(
'status LIKE ' => 'Removed',
'status LIKE ' => 'Pending',
'status LIKE ' => 'Completed'
)
should be
'conditions' => array(
'status LIKE' => array('Removed', 'Pending', 'Completed'),
)
Same for any other array key.
Note that some quick debugging of the array reveals this.
Please also see the tons of other stackoverflow questions with the same issue or other areas where basic research could have pointed you in this direction. Taking a look there first can help to resolve the issue in less time.

insert multiple record to a table with codeigniter

I want to insert the records (that i got from a table) to another table using codeigniter.
here's the function to add the record. I pass the $nokw to insert to another table as foreign key.:
function add_detail($nokw){
$id_sj = $this->session->userdata('id');
$upddate = date('Y')."-".date('m')."-".date('d')." ".date('H').":".date('i').":".date('s');
$i=0;
$this->suratjalan->where('IDDeliveryNo',$id_sj);
$rec = $this->suratjalan->get("t02deliveryno_d")->result_array();
// parse the result and insert it into an array
foreach ($rec as $det){
$i++;
$detail[$i] = array(
'ID' => '',
'NoKwitansi' => $nokw,
'TypeProduct'=> $det['TypeProduct'],
'PartNo' => $det['PartNo'],
'PartNoVendor'=> $det['PartNoVendor'],
'SerialPanel' => $det['SerialPanel'],
'Description' => $det['Description'],
'Dimension' => $det['Dimension'],
'DescriptionVendor' => $det['DescriptionVendor'],
'DimensionVendor' => $det['DimensionVendor'],
'PrintedProduct' => $det['PrintedProduct'],
'Qty' => $det['Qty'],
'UoM' => $det['UoM'],
'Remark' => $det['Remark'],
'UpdUser'=> $this->session->userdata('user'),
'UpdDate'=> $upddate
);
// insert the record
$this->finance->insert('t02fkpd',$detail[$i]);
}
}
It works, but it doesn't work if more than one row is returned from the table 't02deliveryno_d'. I think the error comes when i insert the record. i use the $i++to make different index in $detail array.
How can I fix this to properly insert multiple rows?
You didn't show the db schema, but I'm assuming that t02fkpd.ID is an auto-incrementing column.
If that's the case, the problem is that you're specifying a blank value for ID instead of letting the database handle it. This is probably resulting in attempts to insert duplicate rows with the same (blank) id.
Here's an updated version of your function that I suspect will work better:
function add_detail($nokw) {
$id_sj = $this->session->userdata('id');
$upddate = date('Y-m-d H:i:s');
$this->suratjalan->where('IDDeliveryNo',$id_sj);
$rec = $this->suratjalan->get("t02deliveryno_d")->result_array();
foreach ($rec as $det) {
$details = array(
'NoKwitansi' => $nokw,
'TypeProduct'=> $det['TypeProduct'],
'PartNo' => $det['PartNo'],
'PartNoVendor'=> $det['PartNoVendor'],
'SerialPanel' => $det['SerialPanel'],
'Description' => $det['Description'],
'Dimension' => $det['Dimension'],
'DescriptionVendor' => $det['DescriptionVendor'],
'DimensionVendor' => $det['DimensionVendor'],
'PrintedProduct' => $det['PrintedProduct'],
'Qty' => $det['Qty'],
'UoM' => $det['UoM'],
'Remark' => $det['Remark'],
'UpdUser'=> $this->session->userdata('user'),
'UpdDate'=> $upddate
);
$this->finance->insert('t02fkpd',$details);
}
}
Beyond removing the ID value, I also made the following minor changes:
I removed $i and just reused the same variable for building the array of values to insert. You're not using the array after you insert, so there's no need to build a list of all the rows - you can just overwrite it each time.
I changed your the $upddate calculation to only call date() once. You can specify an entire format string in one call - you're not restricted to just a single character at a time.
I haven't get your question properly. But i think http://ellislab.com/codeigniter/user-guide/database/active_record.html#insert with definitely help you.
You can make a array and pass it to insert_batch function with the table name and array. This will definitely help you.
if you must have checked user-guide for codeigniter. its one of the good documentation, where each and every function is documented.

Convert mysql result (big associative array) to nested arrays

I have a simple query like this
SELECT hometeam.name AS hometeamName, hometeam.shortname AS hometeamShortName,
roadteam.name AS roadteamName, roadteam.shortname AS roadteamShortName,
smatch.startdate
FROM smatch
JOIN team hometeam
ON smatch.hometeamid = hometeam.uid
JOIN team roadteam
ON smatch.roadteamid = roadteam.uid
which be default returns a one dimensional array, like this:
array(5) {
["homeTeamName"] => "Brasil"
["homeTeamShortName"] => "BRA"
["roadTeamName"] => "Norway"
["roadTeamShortName"]=> "NOR"
["startdate"]=> "1309709700"
}
Question is, is there a mysql-way to let the result be a nested array, with a structure like the following?
result =>
hometeam =>
name
shortname
roadteam =>
name
shortname
startdate
And if not, is there a (php) post processing best practice to do this conversion?
Many thanks,
Robson
I don't think there's a way to directly get the result you want, but to generate that array after the query shouldn't be hard.
Something like this?
foreach($rows as $row) {
$resultArray[] = array(
'hometeam' => array(
'name' => $row['homeTeamName'],
'shortname' => $row['homeTeamShortName']
),
'roadteam' => array(
'name' => $row['roadTeamName'],
'shortname' => $row['roadTeamShortName']
),
'startdate' => $row['startdate']
);
}
MySQL (or any database) has no concept of arrays.
The only way I can think of is that you'd need to do it in a Stored Procedure and serialise the result in some way - essentially creating a serialised string... wich you'd then need to re-construct the array from.
However, would be better doing this in PHP to be honest, the gain from doing it in an SP will be minimal. Will be more efficient to query the main and then query within using loops PHP-side.
You have to fetch the result in PDO::FETCH_CLASS mode.
by specifying the __set()-method respectively you can store the result-field-data how ever you like.
This class should also extend ArrayAccess so you can access it like an array or have it return one.

Pagination using multiple searcing criteria in codeigniter

Im trying to implement pagination using multiple searching criteria.
Supposed I Have student table. I also use pagination when the list of student displayed.
The pagination link is. site_url . '/student/page/'; so I use $config['uri_segment'] = 1;
so the pagination link will be
1
2
and son.
After that I wanna search student data using 3 searching criteria implemented using textfield.
id name address.
user can search by id or name or address or combination of the three criteria.
the url become
http://mysite/index.php/student/page/0
href=http://mysite/index.php/student/page/1
and son.
but I use get method for searching. and while trying to search using the search criteria field the url become
href="http://mysite/index.php/student/page/1?id=1&name=a&address=b
the problem occurred when I try create pagination based on criteria. because the pagination link have contain query string
i don't know how to create become
href="http://mysite/index.php/student/page/0?id=1&name=a&address=b
href="http://mysite/index.php/student/page/1?id=1&name=a&address=b
or do you have a best practice to solve this problem ?
Hi phill ....
I have try your suggestion.
$array = array('id' => '001', 'name' => 'a', 'address' => 'canada');
the url become
id/001/name/a/address/canada. I use $this->uri->uri_to_assoc() function to get key and value of the segment.
array (
id => 001,
name=>a,
address=>canada
)
but while there some searching criteria that not included while searching. let say, the user only search by name and address. the array become
$array = array('id' => '', 'name' => 'a', 'address' => 'canada'); and the url id/name/a/address/canada
the assoc array become
array (
id => name,
a=>address,
canada=>
)
the assoc array is not disorganized again. so I can't get the right value of the assoc array.
I think i will set the identifier to the searching criteria if not included. supposed i put #.
if isset($_GET['id']) then
$id = '#'
else
$id = $_GET['id']
$array = array('id' => $id, 'name' => 'a', 'address' => 'canada');
How about that ... ? or if there are another best practice ?
For that I would use $this->uri->uri_to_assoc():
index.php/user/search/name/joe/location/UK/gender/male
Using this function you can turn the
URI into an associative array with
this prototype:
[array]
(
'name' => 'joe'
'location' => 'UK'
'gender' => 'male'
)
See the full documentation here.
You can still use page/1 as the first lot then have /name/whatever afterwards.
Actualy, we can create pagination with multiple and unlimited criteria. Read here http://dengkul.com/2010/07/08/codeigniter-pagination-with-multiple-unlimited-searching-criteria/

Categories