XPath multiple query, parsing based on class and in order - php

Current Situation :
I'm trying to parse a DomDocument with XPath, the result should be an array with Categories and Subcategories .
The problem is, the person that made the HTML did not structure the info with the subcategories in the main categories, they are just delimited by pure css .
The html loos like this :
<div class="menu_item">Main Category AC</div>
<div class="submenu_div">
<a href="http://www.link.com/313">
<div class="sub_item">
<h3>Sub Categ A</h3>
</div>
</a>
<a href="http://www.link.com/475">
<div class="sub_item">
<h3>Sub Categ B</h3>
</div>
</a>
<a href="http://www.link.com/321">
<div class="sub_item">
<h3>Sub Categ C</h3>
</div>
</a>
</div>
<div class="menu_item">Main Category BC</div>
<div class="submenu_div">
<a href="http://www.link.com/313">
<div class="sub_item">
<h3>Sub Categ X</h3>
</div>
</a>
<a href="http://www.link.com/475">
<div class="sub_item">
<h3>Sub Categ Y</h3>
</div>
</a>
<a href="http://www.link.com/321">
<div class="sub_item">
<h3>Sub Categ Z</h3>
</div>
</a>
</div>
Now, with this php I can extract de categories and subcategories, but it's just a list, I don't know what subcategory is in what category, and I'm stuck .
How can I use Xpath to do extract the main category subcategories and assign a parent to every subcategory ?
$doc = new DomDocument;
#$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
foreach( $xpath->query('//div[#class="menu_item"]|//div[#class="submenu_div"]/a/div/h3') as $e ) {
echo $e->nodeValue, "<br />\n";
}

This is a sketch for a solution using XPath. The outer loop looks for the categories and prints them. It also keeps track of the position of the outer div in variable $i. The inner loop constructs another XPath that selects the $i'th div tag, then goes to the following sibling and finally descends to the subcategory text.
Note that you still have to store this data into an appropriate data structure. I'm not familiar with PHP so I cannot help you a lot there.
$i = 0;
foreach( $xpath->query('//div[#class="menu_item"]/text()') as $category ) {
$i = $i + 1;
echo "Category: " . $category->nodeValue . "\n";
foreach ( $xpath->query('//div[#class="menu_item"][' . $i . ']/following-sibling::div[1][#class="submenu_div"]/a/div/h3/text()') as $subcategory) {
echo " Subcategory: " . $subcategory->nodeValue . "\n";
}
}

Based on the answer above, I have made some modifications to also include a for loop and also get the link :
for ($i = 0; $i <= 25; $i++) {
foreach( $xpath->query('//div[#class="menu_item"]['.$i.']/text()') as $category ) {
echo $i . " Category: " . $category->nodeValue . "<br/>\n";
foreach ( $xpath->query('//div[#class="menu_item"][' . $i . ']/following-sibling::div[1][#class="submenu_div"]/a') as $subcategory) {
echo '-----'. $i . " Subcategory: " . $subcategory->nodeValue . "<br/>\n";
echo '-----'. $i . " Link: " . $subcategory->getAttribute("href") . "<br/>\n";
}
echo "<br/>";
}
}
thanks again Marcus Rickert !

Related

Using Xpath to return multiple elements value

I have a result from a curl request from a page like this:
$result =
<div class="c-wrapper">
<a href="link-to-a-page.php">
<div class="c-content-img">
<img src="...">
</div>
<div class="c-link-data">
<div class="c-link-data-title">
<h4>TITLE</h4>
</div>
</div>
</a>
<div>
<div class="c-wrapper">
<div class="c-content-img">
<img src="...">
</div>
<div class="c-link-data">
<div class="c-link-data-title">
<h4>TITLE 2</h4>
</div>
</div>
<div>
Now I have to count how many c-wrapper is present:
I use correctly this:
$doc = new DOMDocument();
#$doc->loadHTML($result);
$xpath = new DOMXPath($doc);
$divs = $xpath->query("//div[contains(#class, 'c-wrapper')]");
echo $divs-length; //<--- printed: 2
Then I have to print all titles:
I use correctly this:
$titles = $xpath->query("//div[contains(#class, 'c-link-data-title')]/h4");
foreach ($titles as $title) {
echo $title->textContent . "<br>";
}
Now the part I don't know: In the first div is present a link, in the second one no link. I'd like to edit my print of titles like this:
foreach ($titles as $title) {
if ( $link_extracted !="" )
echo "<a href='" . $link_extracted . "'>" . $title->textContent . "</a><br>";
else
echo $title->textContent . "<br>";
}
How can I edit $titles = $xpath->query("//div[contains(#class, 'c-link-data-title')]/h4"); to achieve this?
Rather than doing this in separate stages, the code finds the c-wrapper elements and then further uses XPath to find the various parts you want inside that particular element, so in
$link_extracted = $xpath->evaluate("a/#href", $div)[0];
it is looking for an <a> element relative to the $div element. Using [0] as you want only the first one.
$doc = new DOMDocument();
#$doc->loadHTML($result);
$xpath = new DOMXPath($doc);
$divs = $xpath->query("//div[contains(#class, 'c-wrapper')]");
echo $divs->length;
foreach ( $divs as $div ) {
$link_extracted = $xpath->evaluate("a/#href", $div)[0];
$title = $xpath->evaluate("descendant::div[contains(#class, 'c-link-data-title')]/h4/text()"
, $div)[0];
if ( !empty($link_extracted->nodeValue) ) {
echo "<a href='" . $link_extracted->nodeValue . "'>" . $title->textContent . "</a><br>";
}
else {
echo $title->textContent . "<br>";
}
}
which for your test HTML gives...
2<a href='link-to-a-page.php'>TITLE</a><br>TITLE 2<br>

How to fetch news from a db and print them on a PHP page?

I want to fetch the last two news from a DB, then print them on a PHP page using a function.
The two news should be printed one next to the other.
This is what I've done so far, what am I doing wrong?
This is the problem:
<?php
function getAllNewsHome(){
$connection = mysqli_connect("localhost", "root", "", "serverName");
$connection->query("SET NAMES 'utf8'");
$rsNewsHome = mysqli_query($connection, "SELECT * FROM news ORDER BY id DESC LIMIT 2");
$newsHome = mysqli_fetch_all($rsNewsHome, MYSQLI_ASSOC);
mysqli_close($connection);
for($i=0; $i < count($newsHome); $i++){
echo "
<div class='container'>
<div class='row'>
<div class='col-xs-6'>
<h1>".$newsHome[$i]['title']."
</h1>
<p>".$newsHome[$i]['content']."
</div>
<div class='col-xs-6'>
<h1>".$newsHome[$i]['title']."
</h1>
<p>".$newsHome[$i]['content']."
</div>
</div>
</div>
";
}
}
?>
This might be the easiest: increment $i inside of your loop, then make sure that it exists before you echo it out. This will allow you to keep your divs aligned:
for($i = 0; $i < count($newsHome); $i++) {
echo "
<div class='container'>
<div class='row'>
<div class='col-xs-6'>
<h1>" . $newsHome[$i]['title'] . "
</h1>
<p>" . $newsHome[$i]['content'] . "</p>
</div>";
$i++;
echo "
<div class='col-xs-6'>";
if(array_key_exists($i, $newsHome)) {
echo "<h1>" . $newsHome[$i]['title'] . "
</h1>
<p>" . $newsHome[$i]['content'] . "</p>";
}
echo "
</div>
</div>
</div>
";
}
It's doing exactly what you told it to do! Inside the for($i=0; $i < count($newsHome); $i++) loop it produces 2 divs, each containing the same "news" ( $newsHome[$i]['title']) twice!
You want the first div to contain the first article and the second div to contain the second article.
If you remove the second div within the for loop, it will create one div for each "news".
(And you will likely have to move the 2 outer divs (<div class='container'> and <div class='row'> outside the loop)

Foreach limit to word

Im new here , looking for solution for this part of code to just make loop when title is = style or colour .
foreach ($pp_fileds as $f) {
if ( !empty($f['title']) and !empty($f["id"]) and array_key_exists("owlabpfl_".$f["id"] , $owlabpfl_meta) ){
echo '
<li>
<div class="list-label">'.__($f['title'],'toranj').'</div>
<div class="list-des">'.$owlabpfl_meta["owlabpfl_".$f["id"]][0].'</div><div class="list-des"><img src="http://factorymarco.ie/assets/'.__($f['title'],'toranj').'/'.$owlabpfl_meta["owlabpfl_".$f["id"]][0].'.jpg" alt="'.$owlabpfl_meta["owlabpfl_".$f["id"]][0].'"></div>
</li>
';
}
}

PDO MySQL product loop

Im a real beginner when it comes to queries and PDO, i need some assistance in a project i am busy with. What i want to accomplish is to display products from the database. However the template style i am using forces me to show 3 products per row, is there a way to show 3 products per row and loop the code once there is more than 3 (if there is 5 products, the first 3 will display in the first row, and the rest in the second).
Here is the template i am using, note the div class "top-box", this forces that only 3 products can be shown per row.
<div class="top-box">
<?php
$sql = "SELECT * FROM _products WHERE category = '$cat'";
$result = dbConnect()->query($sql);
// If the SQL query is succesfully performed ($result not false)
if($result !== false) {
$cols = $result->columnCount(); // Number of returned columns
// Generate ADS Feed for each ROW
foreach($result as $row) {
echo '<div class="col_1_of_3 span_1_of_3">
<a href="product.php?i=' . $row['model'] . '">
<div class="inner_content clearfix">
<div class="product_image">
<img src="images/' . $row['model'] . '.jpg" alt=""/>
</div>
<div class="price">
<div class="cart-left">
<p class="title">' . $row['name'] . '</p>
</div>
<div class="clear"></div>
</div>
</div>
</a>
</div>';
}
} else {
echo "<p>No products available at this moment, contact us for more information!</p>";
}
?>
<div class="clear"></div>
</div>
You can solve it like this:
<?php
if ($counter % 3 == 0 && $counter!=$total_row_fetched) {
echo '<div class="clear"></div>';
echo '</div>';
echo '<div class="top-box">';
}
if($counter==$total_row_fetched){
echo '<div class="clear"></div>';
echo '</div>'; // it will close the last open div
}
++$counter;
?>
You can use a count variable inside your products foreach-loop. Every three products you can close the top-box and open a new one (This is an assumption as I don't know exactly how your styles work).
At the end of the foreach-loop:
if ($count != 0 && $count % 3 == 0) {
echo '<div class="clear"></div>';
echo '</div>'; // close last .top-box
echo '<div class="top-box">';
}
++$count;
I don't see how your question is connected to PDO. Keep in mind that using unescaped variables inside a query is potentially dangerous. Have a look here for some help: https://stackoverflow.com/a/60496/2516377
Add a counter and use % arithmetic operator to calculate column number.
$counter=0;
foreach (...) {
$column_number=$counter % 3;
$counter++;
}

Displaying each item in an array a number of times

Hi guys this seems to be a very basic question,
I have an array of categories, and would like to display each one 4 times.they are all floating left, so each category is displayed as a row of 4 boxes.
however there are some divs which are boxes with border that are displayed with each iteration. It seems to be working but i dont know if it's the correct way to do it, as all the
categories are not organised. I want a header of the specific category to appear first thing each time the loop jumps to the next item in array;
Any help is welcome. please see code below.
[CODE]
<section class="prodList">
<?php
$loop = 4;
$prodNum = 0;
$categories = array("All","Clothes","Gadgets","Games");
foreach($categories as $cat){
echo "<h2> new cat: ".$cat."</h2>";
while($prodNum < count($categories)*$loop){
echo "<div class='viewerWishlistProdContainer'>
<div class='prodImageContainer'>
</div>
</div>
";
echo " <p>
<button class='sortBtns' onClick='alert(\'See all V'\)'>See all V</button>
</p>";
$prodNum ++;
}
}
[/CODE]
Would this work?
<section class="prodList">
<?php
$categories = array("All","Clothes","Gadgets","Games");
foreach($categories as $cat){
/** Since you know it should repeat four times, why not echo 4 times? */
echo "<h2> new cat: ".$cat."</h2>";
echo "<div style='display:inline-block;padding:5px'>". $cat . "</div>";
echo "<div style='display:inline-block;padding:5px'>". $cat . "</div>";
echo "<div style='display:inline-block;padding:5px'>". $cat . "</div>";
?>
<!-- You don't have to echo plain HTML -->
<div class='viewerWishlistProdContainer'>
<div class='prodImageContainer'>
</div>
</div>
<p>
<button class='sortBtns' onClick='alert(\'See all V'\)'>See all V</button>
</p>
<?php } /** closing the foreach block */ ?>
</section>

Categories