I am trying to produce a web report using PHP that takes a query from a SQL Server database for the main query, and another query from a PostgreSQL database.
What is the best way to merge the results? Currently I am outputting the results as arrays.
The two arrays are made up like so:
Array1
(
[0] => Array
(
[site_name] => TESTSITE
[status] => 1
)
)
Array2
(
[0] => Array
(
[site_name] => TESTSITE
[booking_id] => 2156
[jobresults_key] => 1239
[result] => 4
)
)
The common items are [site_name] in both arrays, and I want to display the [status] from array1 if there is nothing for the matching [site_name] in array2, if there is a matching [site_name] in array2 then it should display [result].
As long as the arrays are short and array1 is always larger, this should work fine.
$array1 = array(
array(
'site_name' => 'TESTSITE',
'status' => 1
),
array(
'site_name' => 'TESTSITE-2',
'status' => 1
)
);
$array2 = array(
array(
'site_name' => 'TESTSITE',
'booking_id' => 2156,
'jobresults_key' => 1239,
'result' => 4
)
);
foreach($array1 as &$item1){
foreach($array2 as $item2){
if( $item1['site_name'] === $item2['site_name'] ){
$item1 = array_replace($item1, $item2);
}
}
}
print_r($array1);
Related
I have this array from a sql query :
[0] => Array
(
[T1] => Array
(
[First] => A
[Second] => Apples
[LastChild] => F
)
[0] => Array
(
[LastChildNb] => 23
)
)
I would like to have this result :
[0] => Array
(
[0] => Array
(
[First] => A
[Second] => Apples
[LastChild] => F
[LastChildNb] => 23
)
)
How do I do this ? I think I should use "hash::combine", but what would the code be ?
You could do something like this, with $arr being your array above:
$arr = array_reduce($arr, function(&$arr, $v) {
return array_merge($arr, (array) $v);
}, array());
You can do
array_merge($arr[0]['T1'], $arr[0][0])
Where $arr is defined as follows:
$arr = [0 => Array
(
'T1' => Array
(
'First' => 'A',
'Second' => 'Apples',
'LastChild' => 'F'
),
0 => Array
(
'LastChildNb' => 23
)
)];
For multiple records in $arr, you can simply cycle over all records and do this merge manually.
However, assuming you're getting the array as a result of a find() method, I suggest you consider using T1__LastChildNb as an alias within the 'fields' of your condition. Simply said, if you have a find like following:
$this->T1->find('all', ['fields' => 'T1.*, (SOME SUBQUERY) AS LastChildNb']);
then modifying it to be
$this->T1->find('all', ['fields' => 'T1.*, (SOME SUBQUERY) AS T1__LastChildNb']);
might be what you're looking for since it will return desired array directly (tested on 2.6).
Let me know if you're interested in more information.
I have 2 table booking and message , now I want to show booking request and message in inbox at a time.
$this->paginate = array(
'conditions' => $conditions,'limit' =>10,
'order'=>array('Booking.created'=>'DESC'));
$bookings = $this->paginate('Booking');
$this->paginate = array(
'conditions' => $conditions,'limit' =>10,
'order'=>array('MessageDetail.created'=>'DESC'));
$messages = $this->paginate('MessageDetail');
i have merge both table data ( array_merge($bookings, $messages); )
now i want to it sort date wise (or any conditions)
Array
(
[0] => Array
(
[Booking] => Array
(
[id] => 4
[host_id] => 21
[place_id] => 10
[room_id] => 13
[user_id] => 12
[message_detail_id] => 16
[created] => 2013-04-23 14:44:03
[accept_date] =>
[cancel_date] =>
)
)
[1] => Array
(
[Booking] => Array
(
[id] => 3
[host_id] => 21
[place_id] => 10
[room_id] => 13
[user_id] => 12
[message_detail_id] => 13
[created] => 2013-04-15 14:10:59
[accept_date] => 2013-04-15 14:40:47
[cancel_date] =>
)
)
[2] => Array
(
[MessageDetail] => Array
(
[id] => 17
[message_id] => 2
[user_id] => 12
[sender_id] => 21
[unread] => 0
[created] => 2013-04-24 12:11:47
)
)
[3] => Array
(
[MessageDetail] => Array
(
[id] => 15
[message_id] => 2
[user_id] => 12
[sender_id] => 21
[booking_id] => 3
[unread] => 0
[created] => 2013-04-15 15:01:12
)
)
)
Thanks in advance.
Option #1:
Create a third Model named "BookingAndMessage". You could use the Model's afterSave method (on both Booking and Message) to create a duplicate record in your new model. You could then query the BookingAndMessage in the proper sort order.
Option #2: To solve the problem, as it stands, you will want to use PHP's usort function (also explained here PHP Sort a multidimensional array by element containing date).
<?php
$BookingsAndMessages = array_merge($bookings, $messages);
function date_compare($a, $b)
{
$modelKeyA = array_key_exists('Booking',$a) ? 'Booking' : 'MessageDetail';
$modelKeyB = array_key_exists('Booking',$b) ? 'Booking' : 'MessageDetail';
$t1 = strtotime($a[$modelKeyA]['created']);
$t2 = strtotime($b[$modelKeyB]['created']);
return $t1 - $t2;
}
usort($BookingsAndMessages, 'date_compare');
// Would sort the merged records by `created` date
?>
The downside to option 2 is, you are going to have to create rules for each field (and sort direction).
Perhaps you should Union your queries first, the Order.
$bookings = $this->Bookings->find('all', [
'conditions' => $conditions,'limit' =>10,
]);
$messages = $this->find('all', [
'conditions' => $conditions,'limit' =>10
]);
$data = $bookings->union($messages);
// This works if your first selected field is the one you want to sort.
$data->epilog('ORDER BY 1 ASC');
reference:
How do you modify a UNION query in CakePHP 3?
I have two controllers: events and results. Events hasMany results, results belongsTo Events. I can save just fine, but when I go to edit, I can only get the information for the Event part of the form, to come in automagically.
I build the Results form info like this:
$option_number = 5;
for ($i = 0; $i < $option_number; $i++) {
echo $this->Form->select("Result.{$i}.object_id", $qual_options, array('empty' => false, 'class' => 'result-name'));
echo $this->Form->hidden("Result.{$i}.id");
echo $this->Form->hidden("Result.{$i}.type", array('value' => 'qual'));
echo $this->Form->hidden("Result.{$i}.action", array('value' => 'add')); ?>
}
In the backend, when I'm doing this to get the automagic population:
if ($this->request->is('get')) {
$this->request->data = $this->Event->findById($id);
}
It works just fine, but I can't figure out how to get it to show the Results. I've tried many things, most probable being:
$this->request->data['Result'] = $this->Result->findAllByEventId($id);
With that, I end up with a data structure like:
[Result] => Array
(
[0] => Array
(
[Result] => Array
(
[id] => 1
[object_id] => 1
[type] => qual
[action] => add
[amt] => 10
[event_id] => 1
)
)
[1] => Array
(
[Result] => Array
(
[id] => 2
[object_id] => 2
[type] => qual
[action] => add
[amt] => 1
[event_id] => 1
)
)
... etc.
)
)
Which definitely looks fishy, I just can't seem to manipulate it to work.
UPDATE I should have mentioned this; this is what my data looks like when I SAVE it, and I want to mimic this!
[Result] => Array
(
[0] => Array
(
[object_id] => 1
[type] => qual
[action] => add
[amt] => 0
[event_id] => 3
)
[1] => Array
(
[object_id] => 1
[type] => qual
[action] => add
[amt] => 1
[event_id] => 3
)
You can see that each numeric key after just has the information in it; instead, my numeric keys ALSO have an array INSIDE them name Result, and I have no idea how to make that go away properly! :} I could always loop through and build it in the format CakePHP wants, but I want to do it properly. And that single line above is what needs changing, but I have run out of ideas.
What about just using find('first') against the event? Since it's hasMany, it will return the Result model in a single [Result] key with many numeric keys.
$this->request->data = $this->Event->find('first', array(
'conditions' => array(
'Event.id' => $id
),
'contain' => array(
'Result'
)
));
This will return something like:
array(
'Event' => array(
'id' => 1,
'name' => 'event name'
),
'Result' => array(
0 => array(
'id' => 1,
...
),
1 => array(
'id' => 2,
...
)
)
);
You could unset the Event key if you needed to.
Imagine I have 2 models: Item and Category. Category hasMany Items and Item belongsTo Category.
I would like to obtain an array "reversed" on how cakephp normally return it: Usually I get something like
[Item]
...some props...
[Category]
...some props...
While I want something like:
Array
[0][Category]
Array
[0][Item]
...some props...
[1][Item]
[1][Category]
...and so on
How can I obtain this result with find method if possible? Otherwise, how to obtain it?
My current model is much more complex but I think I could apply this teory to all my models (I have a Category => Group => Item relationship).
Thanks for suggestions and answers.
According to the title of your question, if the point is to get only Items that are linked to categories named 'bla', you can just call the find() method on the Category model instead of the Item model.
Instead of
$this->Item->find('all', array('conditions' => array('Category.name' => 'bla')));
do
$this->Item->Category->find('all', array('conditions' => array('Category.name' => 'bla')));
This way it will first filter the corresponding Categories and then get the linked Items. So you will obtain something like
Array
(
[0] => Array
(
[Category] => Array
(
[id] => 1
[name] => bla
...
)
[Item] => Array
(
[0] => Array
(
[id] => 1
...
)
[1] => Array
(
[id] => 2
...
)
)
)
)
EDIT
With three models linked together, it is a bit more complicated. The only solution I can think of to prevent loading a lot of data is to build a query with INNER JOINs:
$categories = $this->Category->find('all', array('fields' => array('Category.id', 'Category.name', 'Item.id', 'Item.name'),
'joins' => array(
array( 'table' => 'groups',
'alias' => 'Group',
'type' => 'inner',
'conditions' => 'Group.category_id = Category.id'
),
array( 'table' => 'items',
'alias' => 'Item',
'type' => 'inner',
'conditions' => array('Item.group_id = Group.id', 'Item.name' => 'bla')
))));
It will return an array like this:
Array
(
[0] => Array
(
[Category] => Array
(
[id] => 2
[name] => Shop
)
[Item] => Array
(
[id] => 1
[name] => Bla
)
)
)
EDIT2
A last word about the structure of the array you'll get: it is not exactly like you wanted. The Item array is at the same level that the Category array. So you may end up with many pairs of Category-Item for the same Category.
But a simple foreach loop would then allow you to build an array with the exact struture you wish and this query has the advantage to get only what is neccessary from the database.
I am trying to use CakePHP 1.3.5's searchable behavior with containable behavior to return search results for a specified model and an associated model (Article belongsTo User).
Ignoring the searchable behavior for a moment, the following call to find():
$this->Article->find('all', array(
'conditions' => array('Article.is_published' => 1),
'fields' => array('Article.id'),
'contain' => array('User.name')
));
Executes this SQL query:
SELECT `Article`.`id`, `User`.`name`, `User`.`id` FROM `articles` AS `Article` LEFT JOIN `users` AS `User` ON (`Article`.`user_id` = `User`.`id`) WHERE `Article`.`is_published` = 1
And returns the following array:
Array (
[0] => Array (
[Article] => Array (
[id] => 10
)
[User] => Array (
[name] => Author Name
[id] => 7
)
)
...
)
Which is exactly what's expected. However, the following call to search():
$this->Article->search($query, array(
'conditions' => array('Article.is_published' => 1),
'fields' => array('Article.id'),
'contain' => array('Article' => array('User.name'))
));
Executes this SQL query:
SELECT `Article`.`id` FROM `search_index` AS `SearchIndex` LEFT JOIN `articles` AS `Article` ON (`SearchIndex`.`model` = 'Article' AND `SearchIndex`.`association_key` = `Article`.`id`) WHERE `Article`.`is_published` = 1 AND MATCH(`SearchIndex`.`data`) AGAINST('search term' IN BOOLEAN MODE) AND `Article`.`id` IS NOT NULL
And returns this array:
Array (
[0] => Array (
[Article] => Array (
[id] => 9
)
)
...
)
Looking at the search() method, it is returning $this->SearchIndex->find('all', $findOptions);. $findOptions contains the following:
Array (
[conditions] => Array (
[Article.is_published] => 1
[0] => MATCH(SearchIndex.data) AGAINST('search term' IN BOOLEAN MODE)
)
[fields] => Array (
[0] => Article.id
)
[contain] => Array (
[Article] => Array (
[0] => User.name
)
)
)
The association isn't getting lost along the way, because inside SearchableBehavior, $this->SearchIndex->Article->belongsTo['User'] is present and intact immediately before and after the call to find() inside the search() method.
The call to search() returns the exact same thing for all of the following values of 'contain':
array('Article' => array('User.name'))
array('Article' => array('User'))
array('Article' => array('User' => array()))
array('Article' => array('User' => array('fields' => array('User.name'))))
array('Article' => array('User' => array('fields' => array('name'))))
Am I doing something wrong? I think I'm using the same format as is instructed in the CakePHP documentation, and I haven't found anything online that suggests that you have to do something special to get search results with associated data.
I know that I could easily achieve the result that I want by just looking up the Users with additional calls to find(), but I'd like to get containable behavior to work like it's supposed to and cut down on unnecessary extra database queries.
When using containable, set the recursive option to "true"
$this->Model->Behaviors->attach("Containable",array("recursive"=>true));