Relationship in php array - php

I am using Gdata API to get Youtube video and comments. The reply is in XML which contains an array inside it.
For video ID and comments the XML response are different. For example I am getting array ID as video ID array and for one ID one or many comments in array.
Array of both video ID and comments is as follow:
foreach ($array as $entry)
{
$videoid = basename($entry);
$video[] = $videoid;
$logger->info('Response From Youtube:videoid=>' . $videoid);
}
$this->view->videoid = $video;
$author = array();
$content = array();
$arraycnt = array();
foreach ($video as $id)
{
$comment = "http://gdata.youtube.com/feeds/api/videos/".$id."/comments";
$sxml1 = simplexml_load_file($comment);
$entries = $sxml1->entry;
foreach ($entries as $a)
{
$author[] = $a->author->name;
$content[] = $a->content;
}
}
And the particular view as follow:
<table>
<tr>
<td>
<?php
for($i=0;$i<$length;$i++)
{
?>
<embed
width="420" height="345"
src="http://www.youtube.com/v/<?php echo $videoid[$i];?>"
type="application/x-shockwave-flash">
</embed>
<?php
}
?>
</td>
<td>
<?php
foreach($content as $cont)
{
?>
<p>Comment:<?php echo $cont;?></p>
<?php
}
?>
</td>
<td>
<?php
foreach($author as $auth)
{
?>
<p>Commented By:<?php echo $auth;?></p>
<?php
}
?>
</td>
</tr>
</table>
How can I show the video and comments in the view like:
videoA1 commentA1 commentA2
videoB1 commentB1 commentB2 commentB3

You can keep the id of the video in $author and $content arrays.
foreach ($video as $id)
{
foreach ($entries as $a)
{
$author[$id][] = $a->author->name;
$content[$id][] = $a->content;
}
}
So you can get comment's authors from specific video id:
foreach ($video as $id)
{
echo $id;
foreach($author[$id] as $auth) {
echo ' ', $auth;
}
}
The same goes with comment's content.
Of course you can extend this solution if you want to have just one array for comment's authors and content, but the logic stays the same.

What you actually have here is that you first iterate over the videos and then per each video you iterate over the comments.
So you have a nested iteration: Level 1: Videos, Level 2: Comments.
As I commented earlier you can create a data-structure that is able to have data that can be iterated that way in a multi-dimensional array. Let's take two Youtube videos here in the following example:
-FRm3VPhseI: The Clean Code Talks - "Global State and Singletons"
RlfLCWKxHJ0: The Clean Code Talks - Don't Look For Things!
So if you would want to create a multi-dimenstional array over these two videos with all their comments, you could choose the following format:
$videos = array(
'-FRm3VPhseI' => array(
'id' => '-FRm3VPhseI',
'title' => 'The Clean Code Talks - "Global State and Singletons"',
'href' => 'http://www.youtube.com/watch?v=-FRm3VPhseI&feature=youtube_gdata',
'comments' => array(
array(
'author' => 'Nelson ThePrimate',
'content' => 'There is a cost for r...',
),
array(
'author' => 'dennisdegreef',
'content' => 'That is also a global...',
),
array(
'author' => 'MorleyCode',
'content' => 'State is unavoidable,...',
),
// ...
array(
'author' => 'Jacob Jensen',
'content' => 'I don\'t quite underst...',
),
array(
'author' => 'unity20000',
'content' => 'Testing is not the on...',
),
array(
'author' => 'drummist180',
'content' => 'Turing machine > line...',
),
),
),
'RlfLCWKxHJ0' => array(
'id' => 'RlfLCWKxHJ0',
'title' => 'The Clean Code Talks - Don\'t Look For Things!',
'href' => 'http://www.youtube.com/watch?v=RlfLCWKxHJ0&feature=youtube_gdata',
'comments' =>
array(
array(
'author' => 'Nikolai Paul',
'content' => 'this guy sometimes so...',
),
array(
'author' => 'Madrid Softwaree',
'content' => 'Learn Selenium , QTP ...',
),
array(
'author' => 'Roger Keulen',
'content' => 'Di: Great as a FXCop ...',
),
// ...
array(
'author' => 'michaeldeng1981',
'content' => 'if I do outsourcing p...',
),
array(
'author' => 'Rico Lelina',
'content' => 'How about loggers? Is...',
),
array(
'author' => 'twistedbydsign99',
'content' => '11:55 it should defin...',
),
),
),
);
It first contains all videos keyed/indexed by the Youtube ID and then inside all the comments. To output this, you only have to nest two foreach clauses:
foreach ($videos as $videoId => $video)
{
printf("%s: %s\n", $videoId, $video['title']);
printf(" Comments (%d):\n", count($video['comments']));
foreach ($video['comments'] as $i => $comment)
{
printf(" #%d %s: %s\n", $i + 1, $comment['author'], $comment['content']);
}
echo "\n";
}
Which will create the following output:
-FRm3VPhseI: The Clean Code Talks - "Global State and Singletons"
Comments (6):
#1 Nelson ThePrimate: There is a cost for r...
#2 dennisdegreef: That is also a global...
#3 MorleyCode: State is unavoidable,...
#4 Jacob Jensen: I don't quite underst...
#5 unity20000: Testing is not the on...
#6 drummist180: Turing machine > line...
RlfLCWKxHJ0: The Clean Code Talks - Don't Look For Things!
Comments (6):
#1 Nikolai Paul: this guy sometimes so...
#2 Madrid Softwaree: Learn Selenium , QTP ...
#3 Roger Keulen: Di: Great as a FXCop ...
#4 michaeldeng1981: if I do outsourcing p...
#5 Rico Lelina: How about loggers? Is...
#6 twistedbydsign99: 11:55 it should defin...
Note that in this output the number of comments are limited to 6 each time because I reduced the amount of comments for the example array. Now compare closely how the structure of the nested, multi-dimensional array is read from with the nested foreach clauses. The outer foreach is reading the video, the inner foreach is reading the comments.
This btw. is the same with building that array, it works as well with two nested iterations. To make this more simple I first create a caching variable and some helper functions:
$gdataFetchCache = [];
$gdataFetch = function ($url) use (&$gdataFetchCache)
{
if (!isset($gdataFetchCache[$url]))
{
$gdataFetchCache[$url] = simplexml_load_file($url);
}
return $gdataFetchCache[$url];
};
$gdataNamed = function ($pattern) use ($gdataFetch)
{
return function ($value) use ($pattern, $gdataFetch)
{
return $gdataFetch(sprintf($pattern, $value));
};
};
$ytVideo = $gdataNamed('http://gdata.youtube.com/feeds/api/videos/%s');
$ytComments = $gdataNamed('http://gdata.youtube.com/feeds/api/videos/%s/comments');
These functions allow to fetch Youtube data more easily inside the nested foreach-es:
$videoIds = ['-FRm3VPhseI', 'RlfLCWKxHJ0'];
$videos = [];
foreach ($videoIds as $videoId)
{
$video = $ytVideo($videoId);
$videoArray = [
'id' => (string)$videoId,
'title' => (string)$video->title,
'href' => (string)$video->link['href'],
'comments' => [],
];
$videos[$videoId] = $videoArray;
foreach ($ytComments($videoId)->entry as $comment)
{
$videos[$videoId]['comments'][] = [
'author' => (string)$comment->author->name,
'content' => (string)$comment->content,
];
}
}
If you compare closely again, it has the same structure as the output code.
This is how you can read and create multi-dimensional arrays for which the count is not known in advance. By iterating you create the code for one element but use it as often as you have elements.
This also works nested.

Related

Rearranging logic that uses multipe foreach conditions

I have some code that takes an rss feed url, expands the rss link and gets the individual links inside that feed xml. After checking if a link exists, i insert it to a table if it does not exist and do nothing if it does. However my code is becoming more unreadable and one more check that requires another foreach it shall be even more unreadable.
This is the code
public function links_cron_job(){
//Get Rss Links
$this->db->select("the_link_itself");
$query = $this->db->get_where("search_engine_links", array("link_type" => "rss"));
foreach ($query->result() as $row){
$url = $row->the_link_itself;
$rss = Feed::loadRss($url);
foreach ($rss->item as $item) {
$this->db->where('the_link_itself',$item->link);
$query3 = $this->db->get('search_engine_links');
if ($query3->num_rows() > 0){
echo 'duplicates are there';
}else{
$data = array(
'link_country' => 'usa',
'the_link_itself' => $item->link,
'link_category' => 'news_website',
'link_added_by' => 'admin',
'link_type' => 'ordinary_link',
'link_local_type' => 'news',
'link_region' => 'countrywide',
'link_city' => 'washington',
'date_added' => $item->timestamp,
'last_updated' => time()
);
$this->db->insert('search_engine_links', $data);
echo 'no duplicates are there';
}
}
}
}
What would be another approach in doing what i am doing?
Normally I would say, just enter a return. But in this case you still might have
work to do in the extra iterations. So in this case at least we know that we are
done with this iteration, so at least we can add a continue statement:
<?php
foreach ($rss->item as $item) {
$this->db->where('the_link_itself',$item->link);
$query3 = $this->db->get('search_engine_links');
if ($query3->num_rows() > 0) {
echo 'duplicates are there';
continue;
}
$data = array(
'link_country' => 'usa',
'the_link_itself' => $item->link,
'link_category' => 'news_website',
'link_added_by' => 'admin',
'link_type' => 'ordinary_link',
'link_local_type' => 'news',
'link_region' => 'countrywide',
'link_city' => 'washington',
'date_added' => $item->timestamp,
'last_updated' => time()
);
$this->db->insert('search_engine_links', $data);
echo 'no duplicates are there';
}

How to display the value of array as string or text not as index value

I have two table one for sections where each section has many questions when i echo i got the section text but the questions as index value 0 1 2
<?php
$result = mysql_query($query);
if ($result) {
$data = array();
while ($row = mysql_fetch_assoc($result)) {
$data[($row['SECTION_NAME'])][][($row['QUES_TEXT'])][] = array(
'SECTION' => $row['SECTION_NAME'],
'QUESTION' => $row['QUES_TEXT']
);
}
foreach ($data as $SECTION => $QUESTIONS) {
echo '<h2>',htmlentities($SECTION),'</h2>';
foreach ($QUESTIONS as $QUESTIONS_TEXT => $TEXT) {
echo '<h2>',($QUESTIONS_TEXT),'</h2>';
}
}
}
?>
You are creating extra nested arrays and also the parens are unneeded:
$data[$row['SECTION_NAME']][][$row['QUES_TEXT']][] = array(
'SECTION' => $row['SECTION_NAME'],
'QUESTION' => $row['QUES_TEXT']
);
Should be:
if (empty($data[$row['SECTION_NAME']]) {
$data[$row['SECTION_NAME']] = array();
}
$data[$row['SECTION_NAME']][$row['QUES_TEXT']] = array(
'SECTION' => $row['SECTION_NAME'],
'QUESTION' => $row['QUES_TEXT']
);
Also please see the comments to your original question, this is no longer a safe way to use MySQL from PHP.

Dynamic variables in looper

This is my simple looper code
foreach( $cloud as $item ) {
if ($item['tagname'] == 'nicetag') {
echo $item['tagname'];
foreach( $cloud as $item ) {
echo $item['desc'].'-'.$item['date'];
}
} else
//...
}
I need to use if method in this looper to get tags with same names but diferent descriptions and dates. The problem is that I dont know every tag name becouse any user is allowed to create this tags.
Im not really php developer so I'm sory if it's to dummies question and thanks for any answers!
One possible solution is to declare a temporary variable that will hold tagname that is currently looped through:
$currentTagName = '';
foreach( $cloud as $item ) {
if ($item['tagname'] != $currentTagName) {
echo $item['tagname'];
$currentTagName = $item['tagname'];
}
echo $item['desc'] . '-' . $item['date'];
}
I presume that your array structure is as follows:
$cloud array(
array('tagname' => 'tag', 'desc' => 'the_desc', 'date' => 'the_date'),
array('tagname' => 'tag', 'desc' => 'the_desc_2', 'date' => 'the_date_2'),
...
);
BUT
This solution raises a problem - if your array is not sorted by a tagname, you might get duplicate tagnames.
So the better solution would be to redefine your array structure like this:
$cloud array(
'tagname' => array (
array('desc' => 'the_desc', 'date' => 'the_date'),
array('desc' => 'the_desc_2', 'date' => 'the_date_2')
),
'another_tagname' => array (
array('desc' => 'the_desc_3', 'date' => 'the_date_3'),
...
)
);
and then you can get the data like this:
foreach ($cloud as $tagname => $items) {
echo $tagname;
foreach($items as $item) {
echo $item['desc'] . '-' . $item['date'];
}
}

A recursive function to sort through parent and child nodes in PHP using a foreach loop on an array

I have a data set stored in an array that references itself with parent-child ids:
id, parent_id, title etc. The top tier has a parent_id of 0, and there can be countless parent-child relationships.
So I'm sorting through this array with a foreach loop within a recursive function to check each array element against its parent element, and I think I've been staring at this method too long.
I do end up with the elements in the correct order, but I can't seem to get my lists nested correctly, which makes me think that the method doesn't really work.
Is this the best route to take?
What can I do to improve and fix this method
Is there another trick that I can apply?
Here is my source:
<div>
<div>Subpages</div>
<ul>
<?php subPages($this->subpages->toArray(), 0) ?>
</ul>
<br>
Add New Subpage
</div>
<?php
function subPages($subpages, $parent){
foreach($subpages as $key => &$page){
$newParent = $page['id'];
//If the current page is the parrent start a new list
if($page['id'] == $parent)
{
//Echo out a new list
echo '<ul>';
echo '<li class="collapsed">';
echo '+';
echo ''.$page['title'].'';
subPages($subpages, $newParent);
echo '</li>';
echo '</ul>';
}
//If the page's parent id matches the parent provided
else if($page['parent_id'] == $parent)
{
//Echo out the link
echo '<li class="collapsed">';
echo '+';
echo ''.$page['title'].'';
//Set the page as the new parent
$newParent = $page['id'];
//Remove page from array
unset($subpages[$key]);
//Check the rest of the array for children
subPages($subpages, $newParent);
echo '</li>';
}
}
}
?>
As always, any assistance is appreciated. Please let me know if something isn't clear.
I doubt that you guys are still looking for a real answer to this, but it might help out others with the same problem. Below is a recursive function to resort an array placing children beneath parents.
$initial = array(
array(
'name' => 'People',
'ID' => 2,
'parent' => 0
),
array(
'name' => 'Paul',
'ID' => 4,
'parent' => 2
),
array(
'name' => 'Liz',
'ID' => 5,
'parent' => 2
),
array(
'name' => 'Comus',
'ID' => 6,
'parent' => 3
),
array(
'name' => 'Mai',
'ID' => 7,
'parent' => 2
),
array(
'name' => 'Titus',
'ID' => 8,
'parent' => 3
),
array(
'name' => 'Adult',
'ID' => 9,
'parent' => 6
),
array(
'name' => 'Puppy',
'ID' => 10,
'parent' => 8
),
array(
'name' => 'Programmers',
'ID' => 11,
'parent' => 4
) ,
array(
'name' => 'Animals',
'ID' => 3,
'parent' => 0
)
);
/*---------------------------------
function parentChildSort_r
$idField = The item's ID identifier (required)
$parentField = The item's parent identifier (required)
$els = The array (required)
$parentID = The parent ID for which to sort (internal)
$result = The result set (internal)
$depth = The depth (internal)
----------------------------------*/
function parentChildSort_r($idField, $parentField, $els, $parentID = 0, &$result = array(), &$depth = 0){
foreach ($els as $key => $value):
if ($value[$parentField] == $parentID){
$value['depth'] = $depth;
array_push($result, $value);
unset($els[$key]);
$oldParent = $parentID;
$parentID = $value[$idField];
$depth++;
parentChildSort_r($idField,$parentField, $els, $parentID, $result, $depth);
$parentID = $oldParent;
$depth--;
}
endforeach;
return $result;
}
$result = parentChildSort_r('ID','parent',$initial);
print '<pre>';
print_r($result);
print '</pre>';
It's a wind down method that removes elements from the original array and places them into result set in the proper order. I made it somewhat generic for you, so it just needs you to tell it what your 'ID' field and 'parent' fields are called. Top level items are required to have a parent_id (however you name it) of 0. I also add a depth marker to each item so that you can format on output.
I will try to help you.
It is possible to compose such relations in one pass:
/**
* Used for "recursive" folding of layout items
* Algorithm of infinite tree (non recursive method)
*
* #param array $items
* #return array
*/
function _foldItems($items) {
$result = array();
foreach ($items as $key => $item) {
$itemName = $item['name'];
if (!isset($item['parent']))
continue;
else {
$parentName = $item['parent']; // it can be either `name` or some `id` of the parent item
if (isset($result[$itemName][$item['sequence']])) {
// Done to eliminate `Warning: Cannot use a scalar value as an array in atLeisure_PropertyImport.class.php`
// Sometimes elements already in the list and have [name] => $count and next line tries to put item in array (item becomes parent)
if ( isset($result[$parentName][$item['parentSequence']]['items'][$itemName]) AND
is_scalar($result[$parentName][$item['parentSequence']]['items'][$itemName])
)
$result[$parentName][$item['parentSequence']]['items'][$itemName] = array();
$result[$parentName][$item['parentSequence']]['items'][$itemName][$item['sequence']] = $result[$itemName][$item['sequence']];
unset($result[$itemName][$item['sequence']]);
} else
$result[$parentName][$item['parentSequence']]['items'][$itemName] = $item['count'];
unset($items[$key]);
} // if //
if (empty($result[$itemName]))
unset($result[$itemName]);
} // foreach //
foreach ($items as $item) { // enumerating rest of the items (single items)
$itemName = $item['itemName'];
if (!isset($result[$itemName]))
$result[$itemName][$item['sequence']] = $item['count'];
}
return $result;
}
Example can be a bit hard to read and to understand because there is really too much code, but I've made this function not so long ago for one project and it seems to be work successfully.
NOTE: It will also work if there are several same items linked to one parent item. It uses item sequence number to avoid aliasing similar values into one.

Get all values with a certain key value [duplicate]

This question already has answers here:
Is there a function to extract a 'column' from an array in PHP?
(15 answers)
Closed last month.
So I've been trying to figure this out for a while now and seem to be stuck. I have a multidimensionalarray and I'm trying to output all values with a certain key. Here is the code:
//My Array:
$services = array(
array( service_name => 'facebook',
service_title => 'Facebook',
service_order => 0,
service_type => 'Social Networking'
),
array( service_name => 'twitter',
service_title => 'Twitter',
service_order => 0,
service_type => 'Social Networking'
),
array( service_name => 'tumblr',
service_title => 'MySpace',
service_order => 0,
service_type => 'Blogging'
)
);
The problem is I want to output the values by service_type for example "Social Networking" or "Blogging" I'm able to do this if I use a foreach statement like so:
foreach($services as $service) {
if ($service['service_type'] == 'Social Networking') {
echo($service['service_title']);
}
if ($service['service_type'] == 'Blogging') {
echo($service['service_title']);
}
}
which works fine until I try to add a title in between types like
foreach($services as $service) {
if ($service['service_type'] == 'Social Networking') {
echo($service['service_title']);
}
echo ('<h3> Blogging</h3>');
if ($service['service_type'] == 'Blogging') {
echo($service['service_title']);
}
}
I've learned enough to know that is because of the way foreach loops work so I've been trying to do this with something like while($service['service_type'] == 'Social Networking') (which creates a fun infinite loop) and various other ways.
Ideally I want to sort the array by service_type and then display the results without having to run the foreach loop in between headers.
Any help on this would be awesome or even other suggestions on ways to do this.
With php > 5.5 you can do
$myData = array(
array(
'service_name' => 'facebook',
'service_title' => 'Facebook',
'service_order' => 0,
'service_type' => 'Social Networking'
),
array(
'service_name' => 'twitter',
'service_title' => 'Twitter',
'service_order' => 0,
'service_type' => 'Social Networking'
),
array(
'service_name' => 'tumblr',
'service_title' => 'MySpace',
'service_order' => 0,
'service_type' => 'Blogging'
)
);
$filtered = array_column($myData, 'service_column');
see: http://www.php.net/manual/en/function.array-column.php
I think you are trying to do something like this:
// Create an array containing all of the distinct "service types"
// from the $services array
$service_types = array();
foreach ($services as $service) {
// If we have not yet encountered this entry's "service type",
// then we need to add it to our new array
if (!in_array($service['service_type'], $service_types)) {
$service_types[] = $service['service_type'];
}
}
if (count($service_types)) {
// For each "service type", we want to echo a header and then
// a list of services of that type
foreach ($service_types as $current_type) {
echo '<h3>'.$current_type.'</h3>';
// Loop through the services and echo those that are
// of the correct type
foreach ($services as $service) {
if ($service['service_type'] == $current_type) {
echo($service['service_title']);
}
}
}
} else {
echo '<h3>No services</h3>'
}
I haven't followed all of your question, but you asked if you can sort the array by service type - so you can build a user defined sorting function.
function sortByServiceType($a, $b) {
// This will sort them by service type alphabetically
return strcmp($a['service_type'], $b['service_type']);
}
uasort($services, 'sortByServiceType');

Categories