Bread Crumbs Nav Links, Recursive Array, PDO - php

I have a database table with two columns for page URL's and URL parents, like this:
URL | Parent
Animal | (null)
Mammal | Animal
Tiger | Mammal
I'm trying to convert it to PDO. This is the original script:
$TopnavTable = 'gz_life';
$TopnavName = 'Taxon';
function get_path($node, $TopnavTable, $TopnavName) {
$result = mysql_query('SELECT Parent FROM ' . $TopnavTable . ' WHERE ' . $TopnavName . '="'.$node.'";');
$row = mysql_fetch_array($result);
$path = array();
if ($row['Parent']!='') {
$path[] = $row['Parent'];
$path = array_merge(get_path($row['Parent'], $TopnavTable, $TopnavName), $path);
}
return $path;
}
switch ($MyPage)
{
case 'ChildPage':
$TopNav = str_replace('Carnivora', '', $TopNav);
break;
default:
break;
}
$mypath = get_path($MyURL, $TopnavTable, $TopnavName);
$MyLink = $mypath;
$MyLink = str_replace('Life', '', $MyLink);
$MyDisplay = $mypath;
for($i=0;$i<count($mypath);$i++){
$TopNav = " ".$MyDisplay[$i]." >";
$That = array('<a href="Life">', '"> ', '>', '<a href="');
$This = array('<a href="/Life/">', '">', '> ', '<a href="/Life/');
$TopNav = str_replace($That, $This, $TopNav);
Someone just helped me upgrade a similar query, where the parent-child relationship is based on URL's. For example, the URL MySite/Topics/Washington/Governor should yield bread crumbs navigation links that look like this:
Topics > Washington > Governor
This is the code:
$TopnavTable = 'pox_topics';
$TopnavName = 'URL';
function get_path($dbh,$node,$TopnavTable, $TopnavName) {
$stmt = $dbh->prepare("SELECT name FROM $TopnavTable WHERE $TopnavName = ?");
$stmt->bindValue(1,$node);
$stmt->execute();
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$row = $stmt->fetch();
$path = array_merge(get_path($pdo,$row['Parent'], $TopnavTable, $TopnavName), $path);
return $path ;
}
$Path2 = explode("/", $path);
$arrlength=count($Path2);
$html =''.$Path2[0].' > ';//First Member
for($x=1;$x<$arrlength-1;$x++) {//Between first & last
$html .= ''.$Path2[$x].' > ' ;
}
$html .= '<span class="navHere"><b>'.$Path2[$x].'</b></span>'; //Last member
$html = str_replace('/'.$MySection.'/'.$MySection.'', '/'.$MySection.'', $html);
echo $html ;
I was advised to read up on hierarchical data # http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ In fact, I think that's what my original script posted at the top is based on.
Anyway, I thought I could modify the PDO script above to make it work with my hierarchical DB table, but I've been striking out, as usual.
Can anyone tell me what I need to change? (I deleted $Path2 = explode("/", $path); of course, but I never made any progress beyond that.)

Related

PHP dynamic breadcrumb script creates wrong URL after switching to PHP 8.0

I'm using a PHP script to create a dynamic breadcrumb navigation. I found it several years ago in another forum where it was originally posted in 2006. I updated it by removing some deprecated functions and it worked well so far. It creates the crumbs from the page URL:
$path = $this->_homepath;
$html ='';
$html .= 'Start';
$parts = explode('/', $_SERVER['PHP_SELF']);
$partscount = count($parts);
for($i = 1; $i < ($partscount - 1); $i++) {
$path .= $parts[$i] . '/';
$title = $parts[$i];
if ($this->_usereplacements == true) {
reset($this->_replacements);
foreach($this->_replacements as $search => $replace) {
$title = str_replace($search, $replace, $title);
}
}
if ($this->_replaceunderlines == true) {
$title = str_replace('_', ' ', $title);
}
if ($this->_ucfirst == true) {
$title = ucfirst($title);
}
if ($this->_ucwords == true) {
$title = ucwords($title);
}
$html .= $this->_separator . '' . $title . '';
}
The current page title, i.e. the last element of the bradcrumb, is generated as follows:
$title = '';
if ($title == '') {
$title = $parts[$partscount-1];
if ($this->_usereplacements == true) {
reset($this->_replacements);
foreach($this->_replacements as $search => $replace) {
$title = str_replace($search, $replace, $title);
}
}
if ($this->_removeextension == true) {
$title = substr($title, 0, strrpos($title, '.'));
}
if ($this->_replaceunderlines == true) {
$title = str_replace('_', ' ', $title);
}
if ($this->_ucfirst == true) {
$title = ucfirst($title);
}
if ($this->_ucwords == true) {
$title = ucwords($title);
}
}
$html .= $this->_separator . '<b>' . $title . '</b>';
I only copied relevant parts of the script and spared eg. the function declarations.
After switching from PHP 7.4 to 8.0 I found that the script is messing up a bit. Consider a page with this url: https://www.myhomepage.com/site1/site2/site3
For the crumbs of site1 the script is now generating the URL https://www.myhomepage.com/site1/site2/site1 insted of https://www.myhomepage.com/site1/, whereas for site2 it shows https://www.myhomepage.com/site1/site2/site1/site2 instead of https://www.myhomepage.com/site1/site2/. As you can see, the whole path https://www.myhomepage.com/site1/site2/ is always added as a prefix before the actual path of the crumb.
I haven't found a solution yet despite looking over this thing for two days. I suppose there have been some changes in PHP 8.0 which causes this behaviour, but I haven't found any clues in the incompatibility list (https://www.php.net/manual/de/migration80.php). I printed §path and $title and they look like they should. When echoing $html after each iteration in the for loop it shows already the wrong URLs. That's why I think that probably the for loop is the problem here. Do you have any suggestions?
Any help on this would be very much appreciated.
I finally was able to figure out what causes the strange behaviour of the script. In the declaration of the breadcrumb constructing function there is $this->_homepath = '/';. Later on I assign $path = $this->_homepath;. When I echo $path its empty instead of showing the aforementioned "/". So I directly set $path = '/'; and now everything works again as it should.

User variables of a function, in another function

I have a Laravel app, which the following PHP code:
public function handle()
{
$post_item->category_id = $source->category_id;
$post_item->featured = 0;
$post_item->type = Posts::TYPE_SOURCE;
$post_item->render_type = $item['render_type'];
$post_item->source_id = $source->id;
$post_item->description = is_array($item['description'])?'':$item['description'];
$post_item->featured_image = $item['featured_image'];
$post_item->video_embed_code = $item['video_embed_code'];
$post_item->dont_show_author_publisher = $source->dont_show_author_publisher;
$post_item->show_post_source = $source->show_post_source;
$post_item->show_author_box = $source->dont_show_author_publisher == 1 ? 0 : 1;
$post_item->show_author_socials = $source->dont_show_author_publisher == 1 ? 0 : 1;
$post_item->rating_box = 0;
$post_item->created_at = $item['pubDate'];
$post_item->views = 1;
$post_item->save();
$this->createTags($item['categories'], $post_item->id);
// This is where I want to add my echo
}
public function createTags($tags, $post_id)
{
$post_tags = PostTags::where('post_id', $post_id)->get();
foreach ($post_tags as $post_tag) {
Tags::where('id', $post_tag->tag_id)->delete();
}
PostTags::where('post_id', $post_id)->delete();
foreach ($tags as $tag) {
$old_tag=Tags::where('title',$tag)->first();
if(isset($old_tag))
{
$pt = new PostTags();
$pt->post_id = $post_id;
$pt->tag_id = $old_tag->id;
$pt->save();
}
else {
$new_tag = new Tags();
$new_tag->title = $tag;
$new_tag->slug = Str::slug($tag);
$new_tag->save();
$pt = new PostTags();
$pt->post_id = $post_id;
$pt->tag_id = $new_tag->id;
$pt->save();
}
}
}
Im trying to echo the the title along with the tags, right after the commented place, but it fails to provide the correct output. I was wondering if Im using the correct way or my workaround is completely wrong.
Im using this code in the commented part:
$tweet = $post_item->title . ' tags: ' . $post_item->tags;
After doing some tests, I realized that if I use
var_dump($tag);
right after
foreach ($tags as $tag)
at my createTags function, it seems that all tags are output correctly.
Im wondering if I can store all $tags inside the createTag function's foreach, under a globally accessed variable that would be used in the initial handle function echoed.
Guessing the post item model has a relation "tags" you could try this:
$tweet = $post_item->title . ' tags: ' . implode(', ', $post_item->tags->toArray());
Also if you would just like to echo the tags on the commented place, try this:
echo implode(',', $item['categories']);
try if $post_item->tags - is array then
$tweet = $post_item->title . ' tags: ' . implode(', ', $post_item->tags);
if - collection then
$tweet = $post_item->title . ' tags: ' . $post_item->tags->join(', ');

PHP - read variable from string then replace with variable from Database

In my PHP forum i want people to insert pictures just by inserting the number/ID of the picture (which they can see in an online photoalbum)
I am looking for a function that can read a string from their posts for example
"bla bla and look at this amazing picture [IMG]234[/IMG] isn't it awesome ..."
then finds the picture in a database with the ID 234 and replaces [IMG]234[/IMG] with
<img src = "path/to/image.jpg" />
preg_replace wouldn't work :( does anyone have an Idea?
thanks for Your help in advance
I would do it in this way:
$images = array();
$post = preg_replace_callback('|\[img\](\d+)\[/img\]|i', function($matches) use(&$images) {
$images[] = $matches[1];
return '__image_' . $matches[1];
}, $post);
if (count($images)) {
// Select images
$imageIds = implode(',', $images);
// DB query
$res = mysql_query("SELECT id, path FROM post_images WHERE id IN ({$imageIds})") or die(mysql_error());
// Replace
while (($row = mysql_fetch_assoc($res))) {
$post = str_replace('__image_'.$row['id'], '<img src=' . $row['path'] . ' />', $post);
}
}
The advantage is that you make only one query to database. This is always important to minimize them to increase performance. if you don't care or you are sure that the number of images in not going to be too high, you can simple use this code:
$post = preg_replace_callback('|\[img\](\d+)\[/img\]|i', function($matches) {
$res = mysql_query("SELECT path FROM post_images WHERE id = {$matches[1]}") or die(mysql_error());
$path = mysql_result($res, 0);
return "<img src='$path' />";
}, $post);
here is what i did:
$find = preg_match_all("!\[img\][0-9]+\[\/img\]!", $post, $matches);
foreach ($matches as $match) {
foreach ($match as $ma) {
$res = str_replace("[/img]","", str_replace("[img]", "",$ma));
$query = "
SELECT
path
FROM
table
WHERE id = '".$res."'
";
$result = mysql_query($query, $conn) or die(mysql_error());
while($line = mysql_fetch_array($result)) {
$path = $line["path"];
$path = "<img src = '".$path. "'></img>";
$post = str_replace ("[img]" . $res . "[/img]", $path, $post);
}
}
}
note: i don't know why preg_match_all creates a 2 depths array
Your code needs to make a db call to get the path from the ID.
Use a tag parser or basic regex to match \[img][0-9]+\[/img]. (preg_match)
Then query your database for the path with the match result as the ID.
Finally, use str_replace to replace the original "[img]$match[/img]" with
Example (pseudo, look these functions up first)
$matches = preg_match_all('\[img][0-9]+\[/img]', $input);
$output = $input;
foreach ($matches as $match) {
$cur = mysql_query("SELECT path FROM table WHERE ID = $match");
$row = mysql_fetch_array($cur);
$output = str_replace('[img]'.$match.'[/img]',$row['path'],$output);
}
Note: this will only work with tags in lowercasse: img and not with IMG, iMG, ImG and so on.
Why doesn't preg_replace work? Not knowing the the details, I would guess that you are not escaping the square brackets. What does your regular expression look like? Try something like
/\[IMG\](\d+)\[/IMG]/
The image id will then be in the group 1, i.e. $1.
Example:
preg_match_all('/\[IMG\](\d+)\[/IMG]/', $post, $matches);
foreach ($matches as $match) {
echo "Image number: " . $match[1] . "\n";
}

Split URL in to page and folder variables using php?

I am currently using the following code to achieve a page, section and class variable from the url.
$domain = 'http://' . $_SERVER['HTTP_HOST'];
$path = $_SERVER['REQUEST_URI'];
$url = $domain . $path;
// page + section + class
$page = basename($url);
$page = $class = str_replace('.php','',$page);
$page = str_replace('-',' ',$page);
if ($path == "/") {
$section = $class = "home";
} else if (basename(dirname($url),"/") == $_SERVER['HTTP_HOST']) {
$section = $page;
} else {
$section = basename(dirname($url),"/");
$section = str_replace('-',' ',$section);
$class = basename(dirname($url),"/") . " " . $class;
}
For example if the url is http://www.mydomain.co.uk/about/ the code will return the following variables:
$page = "about"
$section = "about"
$class = "about"
For http://www.mydomain.co.uk/about/general-info/
$page = "general info"
$section = "about"
$class = "about general-info"
But when I add more depth for example For http://www.mydomain.co.uk/about/general-info/history/ to code produces:
$page = "history"
$section = "general info"
$class = "general-info history"
where ideally I need it to output the following:
$page = "history"
$section = "about general info"
$class = "about general-info history"
or breakdown the sections into as many as needed for example:
$section1 = "about"
$section2 = "general-info"
Hopefully someone can help. If anything is unclear please ask.
What about using more general splitting ?
// Url: http://www.mydomain.co.uk/about/general-info/history/
$slices = explode('/', $_SERVER['REQUEST_URI']);
// $slices == ['about', 'general-info', 'history']
Then do your routing as you want:
$class = implode(' ', $slices);
$section = $slices[1];
// etc.
So you want an URI-scheme as follows?
http://<domain>/<section-1>/<section-2>/.../<page>/
Special cases:
(1) http://<domain>/ -- page is empty, section is "home"
(2) http://<domain>/<section>/ -- page and section are '<section>'
Instead of relying on basename(), you should split your string into an array using explode() or preg_split(). You can use REQUEST_URI directly since the domain name does not give any extra information for your sections and page.
Once you have an array, you can easily count() the number of path components, handle special cases for empty and size-one paths, and so on. In the following example, I use array_pop() to extract the last part of the path to separate the page from the sections. Since you seem to desire space-separated strings for sections and page, I use implode() to join the arrays back into a string.
// No need for the domain stuff!
$path = $_SERVER['REQUEST_URI'];
// Split at '/', could use explode() but the PREG_SPLIT_NO_EMPTY flag is
// very handy since it handles "//" and "/" at start/end.
$tokens = preg_split('#/#', $path, -1, PREG_SPLIT_NO_EMPTY);
if (count($tokens) == 0) {
// Special case 1
$page = "";
$section = $class = 'home';
} elseif (count($tokens) == 1) {
// Specical case 2
$page = $section = $class = $tokens[0];
} else {
// Class contains all tokens.
$class = implode(' ', $tokens);
// The last part is the page.
$page = array_pop($tokens);
// Everything else are sections.
$sections = implode(' ', $tokens);
}
// You seem to want spaces for dashes in the section:
$section = str_replace('-', ' ', $section);

PHP function Absolute URL Display Problem

I'm trying to get my function to display my categories absolute url address for example http://www.example.com/cat/sub-1/sub-2/sub-3/ But I keep getting http://www.example.com/cat//sub-3/ can some one help me correct this problem. Please be detailed as possible since I'm farily new to PHP.
Here is my PHP function
function allCategories(){
global $parent_url;
global $url;
$nodeList = array();
$tree = array();
$query = mysqli_query(database(),"SELECT * FROM categories ORDER BY parent_id, category LIKE '%more%', category ASC");
while($row = mysqli_fetch_assoc($query)){
$nodeList[$row['id']] = array_merge($row, array('children' => array()));
}
mysqli_free_result($query);
foreach($nodeList as $nodeId => &$node) {
if(!$node['parent_id'] || !array_key_exists($node['parent_id'], $nodeList)){
$tree[] = &$node;
$url = $parent_url . $node['url'];
$url = str_replace('?cat=', '', $url);
echo '<li>' . strip_tags($node['category']) . '';
} else {
$nodeList[$node['parent_id']]['children'][] = &$node;
$url = $parent_url . $node['url'];
$cat_num = array('?cat=','&sub1=','&sub2=');
$url = str_replace($cat_num, '/', $url);
echo '<li>' . strip_tags($node['category']) . '';
}
echo '</li>';
}
echo '</ol>';
unset($node);
unset($nodeList);
}
allCategories();
I suspect your query is erroring out at the MySQL level, and you don't have anything set up to tell you so (especially if warnings are turned off in the php.ini file).
Try adding something like this to the line that starts with $query:
or die( "<h1>SELECT query failed!</h1> <p>Error: " . mysqli_error( $dbc ) . "</p>" );
$dbc needs to be replaced with whatever variable holds your database connection.
Obviously, this is for debugging only. You would replace die with some error-handling function on a production server.

Categories