How to build a family tree with colspan in PHP - php

I have question about looping out a family tree in PHP. I want the structure to be like this:
See picture
I have truble presenting the data in this way. This is my code:
$start_person = $_GET['search'];
// Hämta alla personer från databasen
$db = new PDO("mysql:host=localhost;dbname=csv2", "root", "");
$stmt = $db->prepare("SELECT * FROM horses");
$stmt->execute();
$persons = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Skapa en array för varje generation och lägg till startpersonen i den första generationen
$generations = array();
$generations[0] = array_filter($persons, function ($p) use ($start_person) {
return $p['Name'] === $start_person;
});
// Loopa igenom varje generation och lägg till personernas föräldrar i nästa generation
for ($i = 1; $i <= 5; $i++) {
$prev_generation = $generations[$i - 1];
$generations[$i] = array();
foreach ($prev_generation as $person) {
$father = $person['Sire_Name'];
$mother = $person['Dam_Name'];
$father_data = array_filter($persons, function ($p) use ($father) {
return $p['Name'] === $father;
});
$mother_data = array_filter($persons, function ($p) use ($mother) {
return $p['Name'] === $mother;
});
if (count($father_data) > 0) {
$generations[$i] = array_merge($generations[$i], $father_data);
}
if (count($mother_data) > 0) {
$generations[$i] = array_merge($generations[$i], $mother_data);
}
}
}
echo '<div class="bg-white rounded-lg shadow-md p-4">';
echo "<table class='bg-white' style='max-width: 700px;'>";
for ($i = 0; $i <= 5; $i++) {
echo "<tr>";
foreach ($generations[$i] as $person) {
// Print out father's generation
echo "<td colspan='" . pow(2, 5-$i) . "' style='border: 1px solid black;'><a style='color:blue' href='query.php?search={$person['Sire_Name']}'>{$person['Sire_Name']}</a></td>";
// Print out mother's generation
echo "<td colspan='" . pow(2, 5-$i) . "' style='border: 1px solid black;'><a style='color:blue' href='query.php?search={$person['Dam_Name']}'>{$person['Dam_Name']}</a></td>";
}
echo "</tr>";
}
echo "</table>";
echo "</div>";
What should I edit to present the data as the picture?

On second thought, a nested table might be easier, although not what you asked for. (Maybe if I had more coffee.)
The class below represents a common structure with a name and two optional leafs, just called left and right in order to not impose any implied logic, although they could be parents, children, sire, dam, etc.
class Thing {
public function __construct(
public readonly string $name,
public readonly ?Thing $left = null,
public readonly ?Thing $right = null,
) {
}
public function hasThings(): bool {
return $this->left || $this->right;
}
public function getThingDepth(): int{
if(!$this->hasThings()) {
return 1;
}
return max($this->left->getThingDepth(), $this->right->getThingDepth()) + 1;
}
public function getHtml(): string {
$buf = [];
$buf[] = '<table>';
$buf[] = '<tr>';
$buf[] = '<td>';
$buf[] = $this->name;
$buf[] = '</td>';
if($this->hasThings()){
$buf[] = '<td>';
$buf[] = $this?->left->getHtml() ?? '-';
$buf[] = $this?->right->getHtml() ?? '-';
$buf[] = '</td>';
}
$buf[] = '</tr>';
$buf[] = '</table>';
return implode("\n", $buf);
}
}
The getThingDepth() method might be useful for the rowspan, so I left it in, although it is not currently used.
Using it is where it gets complicated, and you might need to relax the readonly portions, or change the constructor completely, and that really depends on how you’d be building it programmatically. But here’s a demo usage:
$me = new Thing(
'Child',
new Thing('Dad', new Thing('Dad\'s dad'), new Thing('Dad\'s mom')),
new Thing('Mom', new Thing('Mom\'s dad'), new Thing('Mom\'s mom')),
);
echo $me->getHtml();
You can see an online demo here along with what the rendered HTML looks like here
I know it isn’t what you were initially looking for, but maybe it helps, or maybe puts you down a path at least.

Related

Display values without array (print_r)

I have a function that displays the actors photos from TMDB to my website is there a way that I can make it print without an array, it prints only with print_r I want to know if I can print it like echo or print.
This is the code:
public function getCasts($movieID)
{
if (empty($movieID)) return;
function cmpcast($a, $b)
{
return ($a["order"]>$b["order"]);
}
$temp = $this->_call("movie/" . $movieID . "/casts");
$casts = $temp['cast'];
$temp = array();
if (count($casts) > 0)
{
usort($casts, "cmpcast");
foreach ($casts as &$actor) {
if (!empty($actor['profile_path'])) {
for ($i=6; $i<count($temp['id']); $i++)
if ($temp['name'][$i] == $actor['name']) $temp['char'][$i] .= " / ".str_replace('(voice)', '(hang)', $actor['character']);
if (!in_array($actor['name'], (array) $temp['name'])) {
$temp['pic'][] = "<div style='margin-top:15px;' align='center'><div style='width:140px;margin-right:8px;display:inline-block;vertical-align:top;'><img style='width:130px;height:130px;border-radius:50%;' src='".$this->getImageURL().$actor['profile_path']."'><br />".$actor['name']."<br />".$actor['character']."</div></div>";
}
}
}
}
return $temp;
}
You could use some kind of loop. Use a for loop if you want to limit the number of item you echo.
For example :
$casts = getCasts(1);
for ($i = 0; $i < 5; $i++) {
if (isset($casts['pic'][$i])) {
echo $casts['pic'][$i];
}
}
Hope it helps.

How to insert coordinates of a table into an array in PHP

I am making the game battleships in PHP and I need to make it so that the computer randomly chooses 5 coordinates(x, y), and they have to be in a straight line (column or row). How do I make it so the coordinates are in a line and how do I connect the coordinates to the table cells?
Here's what I have so far:
<?php
session_start();
$i=0;
$start=1;
$st=65;
$l=0;
$polje=array();
$x=(mt_rand(0, 10));
$y=(mt_rand(0, 10));
for($q=0; $q<5; $q++){
$polje[$l]=array($x, $y);
}
echo "<table id='table'>\n";
while ($i<11){
$ascii=chr($st);
echo "<tr>";
for($k=0; $k<11; $k++, $start++){
if($k==0){
echo "<td>$i</td>";
}
else if($i==0){
echo "<td class='td2'>$ascii</td>";
$ascii++;
}
else{
echo "<td class='td1'> </td>";
}
}
echo "</tr>";
$i++;
}
?>
This is what the table looks like:
I like this kind of challenge.
Of course, there is a lot of ways, better ways to do this, with OOP, but you can get the point.
Read the comments through the code and if you have any doubts feel free to ask.
I did some treatment to avoid ships overlapping each other, and random deployment.
const BOARD_SIZE = 10; //10x10 board
const SUBMARINE = 1;
const DESTROYER = 2;
const BATTLESHIP = 3;
const AIRCRAFTCARRIER = 4;
$ships_available = [];
//populate with some data based on game rule
array_push($ships_available, SUBMARINE);
array_push($ships_available, SUBMARINE);
array_push($ships_available, DESTROYER);
array_push($ships_available, DESTROYER);
array_push($ships_available, DESTROYER);
array_push($ships_available, BATTLESHIP);
array_push($ships_available, BATTLESHIP);
array_push($ships_available, AIRCRAFTCARRIER);
$board = [];
while(count($ships_available) > 0) {
deployShip($board, $ships_available);
}
$lastColumnLetter = chr(ord('A')+ BOARD_SIZE-1);
//print table
echo "<table border='1' align='center'>";
echo "<tr><td></td><td>".implode('</td><td>',range(1, BOARD_SIZE))."</td></tr>";
for($i = 'A'; $i <= $lastColumnLetter; $i++) {
echo "<tr><td>".$i."</td>";
for($j=0; $j < BOARD_SIZE; $j++) {
echo "<td align='center'>";
echo $board[$i][$j] ?: ' ';
echo "</td>";
}
echo "</tr>";
}
echo "</table>";
exit;
function array_merge_custom($array1, $array2) {
foreach($array2 as $key => $value) {
foreach($value as $k => $v) {
$array1[$key][$k] = $v;
}
}
return $array1;
}
function deployShip(&$board, &$ships_available) {
$randomShipKey = array_rand($ships_available);
$ship = $ships_available[$randomShipKey];
$beginCoordinates = getRandomCoordinates();
$coordinates = getShipCoordinates($board, $beginCoordinates, $ship);
if(!$coordinates) {
return false;
}
unset($ships_available[$randomShipKey]);
$board = array_merge_custom($board,$coordinates);
}
function getRowNumberByLetter($letter) {
return array_search($letter, range('A','Z'));
}
function getRowLetterByNumber($number) {
$letters = range('A','Z');
return $letters[$number];
}
function getRandomCoordinates() {
return ['row' => chr(mt_rand(ord('A'), (ord('A') + BOARD_SIZE-1))), 'col' => mt_rand(0,BOARD_SIZE -1)];
}
function getShipCoordinates($board, $beginCoordinates, $ship) {
if(isset($board[$beginCoordinates['row']][$beginCoordinates['col']])) {
return false; //anchor position already taken
}
if($ship == 1) {
$return[$beginCoordinates['row']][$beginCoordinates['col']] = 1;
return $return;
}
$shipArraySize = $ship -1;
$directions = ['left', 'right', 'up', 'down'];
$tries = 10;
while($tries > 0) {
$tries--;
$direction = $directions[array_rand($directions)];
$return = [];
switch($direction) {
case 'left':
if(($beginCoordinates['col'] - $shipArraySize) < 0) { //check if can go left
break;
}
for($colBegin = ($beginCoordinates['col'] - $shipArraySize), $colEnd = $beginCoordinates['col']; $colBegin <= $colEnd; $colBegin++) {
if(!empty($board[$beginCoordinates['row']][$colBegin])) {
break 2;
} else {
$return[$beginCoordinates['row']][$colBegin] = $ship;
}
}
return $return;
case 'right':
if(($beginCoordinates['col'] + $shipArraySize) > BOARD_SIZE -1) { //check if can go right
break;
}
for($colBegin = $beginCoordinates['col'], $colEnd = ($beginCoordinates['col'] + $shipArraySize); $colBegin <= $colEnd; $colBegin++) {
if(!empty($board[$beginCoordinates['row']][$colEnd])) {
break 2;
} else {
$return[$beginCoordinates['row']][$colBegin] = $ship;
}
}
return $return;
case 'up':
if((getRowNumberByLetter($beginCoordinates['row']) - $shipArraySize) < 0) { //check if can go up
break;
}
for($rowBegin = (getRowNumberByLetter($beginCoordinates['row']) - $shipArraySize), $rowEnd = getRowNumberByLetter($beginCoordinates['row']); $rowBegin <= $rowEnd; $rowBegin++) {
if(!empty($board[getRowLetterByNumber($rowBegin)][$beginCoordinates['col']])) {
break 2;
} else {
$return[getRowLetterByNumber($rowBegin)][$beginCoordinates['col']] = $ship;
}
}
return $return;
case 'down':
if((getRowNumberByLetter($beginCoordinates['row']) + $shipArraySize) > BOARD_SIZE -1) { //check if can go down
break;
}
for($rowBegin = getRowNumberByLetter($beginCoordinates['row']), $rowEnd = (getRowNumberByLetter($beginCoordinates['row']) + $shipArraySize); $rowBegin <= $rowEnd; $rowBegin++) {
if(!empty($board[getRowLetterByNumber($rowBegin)][$beginCoordinates['col']])) {
break 2;
} else {
$return[getRowLetterByNumber($rowBegin)][$beginCoordinates['col']] = $ship;
}
}
return $return;
}
}
return false;
}

MySQLi Rows Not Loading

i have a basic form that loads 15 dropdown boxes with the same topics in each box. this is a voting page where the user can vote for his favorite topic or his least favorite topic. the problem i have is that the topics arent being loaded when i tell them to. Here is my code.
PHP
<?php
$Vote = new Vote();
class Vote {
public function GetTopic() {
$Connect = new mysqli("127.0.0.1", "root", "", "Data");
$Query = 'SELECT * FROM Topics';
if($Gather = $Connect->query($Query))
{
while($Row = $Gather->fetch_assoc())
{
$Topic = $Row['Topic'];
echo '<option>'.$Topic.'</option>';
}
$Gather->free();
}
else
{
echo 'Error';
}
$Connect->close();
}
public function LoadTopic() {
for($I = 15; $I > 0; $I--)
{
echo '<select><option>'.$I.'</option>'.$this->GetTopic().'</select>';
}
}
}
?>
If you use your function like this you should return your html-data instead of outputting it:
public function GetTopic() {
$Connect = new mysqli("127.0.0.1", "root", "", "Data");
$Query = 'SELECT * FROM Topics';
if($Gather = $Connect->query($Query))
{
$html = "";
while($Row = $Gather->fetch_assoc())
{
$Topic = $Row['Topic'];
$html .= '<option>'.$Topic.'</option>';
}
$Gather->free();
return $html;
} else
{
//handle error
}
$Connect->close();
}
Let's try something a bit more appropriate for a class:
<?php
class Vote
{
private $connect;
public $topics = array();
public function __construct()
{
$this->connect = new mysqli( '127.0.0.1', 'root', '', 'Data' );
if( $this->connect->connect_errno )
{
echo "Error:(" . $this->connect->connect_errno . "): " . $this->connect->connect_error . ".";
}
}
public function GetTopics()
{
$Query = 'SELECT * FROM Topics';
if( $Gather = $this->connect->query( $Query ) )
{
while( $Row = $Gather->fetch_assoc() )
{
$this->topics[] = $Row['Topic'];
}
$Gather->free();
}
else
{
echo 'Error';
}
}
public function LoadTopics()
{
if( $max = count($this->topics) > 0 )
{
$html = "<select>\r\n";
for( $i = 0; $i < $max; ++$i )
{
$html .= "<option value=" . $i . ">" . $this->topics[$i] . "</option>";
}
$html .= "</select>\r\n";
return $html;
}
else
{
return false;
}
}
public function __destruct()
{
$this->connect->close();
}
}
?>
The __construct() / __destruct() methods are practically made to put your connection in. You could have also combined both functions and just have the GetTopics() method (I compulsively changed some method and property names) run the query, format the results and return the $html.
Also, I upgraded your for function, in case you decide to add another entry later to your topics, it will expand with it instead of it counting through 15 static rows.
You can call it with:
<?php
$vote = new Vote();
echo $vote->GetTopics()->LoadTopics();
?>
I saw the answer was already selected, didn't want my work to go to waste though ;D
Alternate GetTopics() function, all rolled into one.
public function GetTopics()
{
$Query = 'SELECT * FROM Topics';
if( $Gather = $this->connect->query( $Query ) )
{
$html = "<select>\r\n";
$i = 0;
while( $Row = $Gather->fetch_assoc() )
{
$html .= "<option value=" . $i . ">" . $Row['Topic'] . "</option>";
++i;
}
$html .= "</select>\r\n";
$Gather->free();
return $html;
}
else
{
return "Error: No Results Returned";
}
}
And now it's just called by:
<?php echo $vote->GetTopics(); ?>

Recursive function - tree view - <ul> <li> ... (stuck)

It seems that I'm stuck with my recursive function.
I have a problem with closing the unnamed list (</ul>) and the list-items (</li>)
The thing what i get is
-aaa
-bbb
-b11
-b22
-b33
-ccc
-c11
-c22
-c33
-ddd
-d11
-d22
-d33
-eee
-fff
And the thing what i want is:
-aaa
-bbb
-b11
-b22
-b2a
-b2c
-b2b
-b33
-ccc
-c11
-c22
-c33
-c2a
-c2c
-c2c1
-c2c2
-c2b
-ddd
-d11
-d22
-d33
-eee
-fff
This is the code that i'm using
$html .= '<ul>';
$i = 0;
foreach ($result as $item)
{
$html .= "<li>$item->id";
$html .= getSubjects($item->id, NULL, "",$i); <--- start
$html .= "</li>";
}
$html .= '</ul>';
And the function
function getSubjects($chapter_id = NULL, $subject_id = NULL, $string = '', $i = 0 ) {
$i++;
// getting the information out of the database
// Depending of his parent was a chapter or a subject
$query = db_select('course_subject', 'su');
//JOIN node with users
$query->join('course_general_info', 'g', 'su.general_info_id = g.id');
// If his parent was a chapter - get all the values where chapter id = ...
if ($chapter_id != NULL) {
$query
->fields('g', array('short_title', 'general_id'))
->fields('su', array('id'))
->condition('su.chapter_id', $chapter_id, '=');
$result = $query->execute();
}
// if the parent is a subject -
// get value all the values where subject id = ...
else {
$query
->fields('g', array('short_title', 'general_id'))
->fields('su', array('id'))
->condition('su.subject_id', $subject_id, '=');
$result = $query->execute();
}
// Because count doesn't work (drupal)
$int = 0;
foreach ($result as $t) {
$int++;
}
// if there no values in result - than return the string
if ($int == 0) {
return $string;
}
else {
// Creating a new <ul>
$string .= "<ul>";
foreach ($result as $item) {
// change the id's
$subject_id = $item->id;
$chapter_id = NULL;
// and set the string --> with the function to his own function
$string .= "<li>$item->short_title - id - $item->id ";
getSubjects(NULL, $subject_id, $string, $i);
$string .="</li>";
}
$string .= "</ul>";
}
// I thougt that this return wasn't necessary
return $string;
}
Does someone have more experience with this kind of things?
All help is welcome.
I am not sure what you are trying to do but here is some code you can test and see if it helps to solve your problem:
This part is just for testing, it makes three dimensional array for testing:
for ($x = 0; $x < 2; $x++) {
$result["c$x"] = "ROOT-{$x}";
for ($y = 0; $y < 3; $y++) {
$result[$x]["c$y"] = "SECOND-{$x}-{$y}";
$rnd_count1 = rand(0,3);
for ($z = 0; $z < $rnd_count1; $z++) {
$result[$x][$y]["c$z"] = "RND-{$x}-{$y}-{$z}";
$rnd_count2 = rand(0,4);
for ($c = 0; $c < $rnd_count2; $c++) {
$result[$x][$y][$z][$c] = "LAST-{$x}-{$y}-{$z}-{$c}";
}
}
}
}
// $result is now four dimensional array with some values
// Last two levels gets random count starting from 0 items.
UPDATE:
Added some randomness and fourth level to test array.
And here is function which sorts array to unordered list:
function recursive(array $array, $list_open = false){
foreach ($array as $item) {
if (is_array($item)) {
$html .= "<ul>\n";
$html .= recursive($item, true);
$html .= "</ul>\n";
$list_open = false;
} else {
if (!$list_open) {
$html .= "<ul>\n";
$list_open = true;
}
$html .= "\t<li>$item</li>\n";
}
}
if ($list_open) $html .= "</ul>\n";
return $html;
}
// Then test run, output results to page:
echo recursive($result);
UPDATE:
Now it should open and close <ul> tags properly.

How to sort XML feed using PHP

I am new this parsing xml feeds; so I have a piece of code that I have been working on that extracts the data from the xml file. Now I want be able to sort the array by the publish date pubdate. I have not found any examples that can anyone help me.
I used sort() which puts it in some random order.
$xml_headline_key = "*ARRAY*CATCHUP*PROGRAMMENAME";
$xml_link_key = "*ARRAY*CATCHUP*ITEMURL";
$xml_description_key = "*ARRAY*CATCHUP*SHORTSYNOPSIS";
$xml_publish_key = "*ARRAY*CATCHUP*ORIGINALAIRINGDATE";
$story_array = array();
$counter = 0;
class xml_story{
var $headline, $description, $link, $pubdate;
}
function contents($parser, $data){
global $current_tag, $xml_headline_key, $xml_link_key, $xml_description_key, $counter, $story_array, $xml_link_key, $xml_publish_key;
switch($current_tag){
case $xml_headline_key:
$story_array[$counter] = new xml_story();
$story_array[$counter]->headline = $data;
break;
case $xml_link_key:
$story_array[$counter]->link = $data;
break;
case $xml_description_key:
$story_array[$counter]->description = $data;
$counter++;
break;
case $xml_publish_key:
$story_array[$counter]->pubdate = $data;
break;
}
}
$corrie_counter = 0;
sort($story_array);
$dateformat = "D j M, g:ia";
for($x=0;$x<count($story_array);$x++){
if($story_array[$x]->headline == "Coronation Street"){
if($corrie_counter != 4){
echo "<li><span class=\"bold\">". date($dateformat, strtotime($story_array[$x]->pubdate)) . "</span>\n";
echo "\t<span class=\"text\">" . trunc($story_array[$x]->description,30, " ") . "</span>\n";
echo "\t";
$corrie_counter++;
}
}
}
You can use uasort, and inside sortByPubdate define the way the two pubdates should be compared.
uasort($story_array, 'sortByPubdate');
// Sort function
function sortByPubdate($a, $b) {
if ($a->pubdate == $b->pubdate) {
return 0;
}
return ($a->pubdate < $b->pubdate) ? -1 : 1;
}

Categories