fbpx

Dzisiaj zajmiemy się zadaniem, które całkiem często można spotkać na rozmowach i testach kwalifikacyjnych. Postaramy się stworzyć dwupoziomowe menu, którego zawartość będzie przechowywana w relacyjnej bazie danych, a kolejne odnośniki będą wskazywać na inny artykuł. Wykorzystamy w tym celu relację w bazie danych. Myślę, że takie ćwiczenie nie tylko pozwoli na lepsze przygotowanie do rozmów kwalifikacyjnych, ale może się sprawdzić jako ciekawe rozwiązanie do naszego systemu CMS. Jeśli chcesz zobaczyć jak powstaje strona z użyciem PHP zapoznaj się z naszym kursem https://blog.edugrafia.pl/kursy/kurs-bootstrap-php-mysql-strona-od-podstaw/

A więc do dzieła…

Pierwszym krokiem będzie przygotowanie przestrzeni roboczej. Oczywiście, jako że korzystamy z bazy danych i PHP, to musimy całość umieścić na serwerze lokalnym. Ja korzystam z serwera Xampp. W katalogu htdocs utworzyłem podkatalog menu i to w nim będziemy pracować

Zacznijmy od naszej bazy danych

Pierwszym krokiem będzie stworzenie dwóch tabel menu oraz artykuly, a także wypełnienie ich danymi. Zacznijmy więc do tabeli menu

Tak prezentuje się jej struktura, wypełnijmy więc ją przykładowymi danymi

Teraz stwórzmy tabele artykuly

Struktura nie jest zbyt skomplikowana

I znów wypełnijmy ją danymi

Stwórzmy strukturę naszego pliku index.php

<?php
require_once('functions.php'); // dołączamy plik z funkcjami
?>
<!doctype html>

<html>
     <head>
          <meta charset="UTF-8" />
          <title>Menu</title>
          <base href="/menu/"> <!-- ustalamy bazową ścieżkę -->
          <link rel="stylesheet" type="text/css" href="./css/styl.css">
     </head>
     <body>
        <main>
            <nav>
            <?php display_menu(); // ta funkcja odpowie za wyświetlanie naszego menu ?>
            </nav>
            <section>
            <h1><?php echo display_article('tytul'); // Wyświetlmy tytuł artykułu ?></h1>
            <article>
                <?php echo display_article('tresc'); // a tu jego treść ?>
            </article>
            </section>
        </main>
            <footer>
            </footer>
     </body>
</html>

Sam kod raczej nie jest zbyt skomplikowany – podstawowa struktura HTML z wstawkami PHP. Jak widać, za całość mechaniki odpowie nasz plik functions.php.

Zajmijmy się więc tym plikiem

<?php
require_once('db_connect.php');
?>

I od razu stwórzmy też kolejny plik. Niech odpowiada on za połączenie z bazą danych. Oczywiście zarówno nazwa pliku jak i sposób połączenia z bazą jest absolutnie dowolny. Ja użyję biblioteki PDO.

db_connect.php

<?php
try {
    $pdo = new PDO("mysql:host=localhost;dbname=menu;charset=utf8", 'root', '');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
catch(PDOException $e)
    {
    echo "Błąd połączenia: " . $e->getMessage();
    }
?>

Oczywiście musimy podać dane dostępowe do bazy danych, przy tworzeniu obiektu klasy PDO.

Jeśli wszystko działa poprawnie, wracamy do pliku z funkcjami

<?php
require_once('db_connect.php');
    function menu($parent_id)
    {
        global $pdo;
        $list = [];
        $rows = $pdo->query("SELECT * FROM menu WHERE id_rodzica=$parent_id;");
        $i=0;
        foreach($rows as $r)
        {
            $list[$i]=$r;
            $list[$i]['children']=menu($r['id']);
            $i++;
        }
        return $list;
    }

Kluczowa dla nas funkcja menu, przypisuje odpowiednim elementom nadrzędnym ich elementy dzieci, czyli w praktyce tworzymy w zmiennej $list tablicę z hierarchią naszego menu. Warto zwrócić uwagę, że posługujemy się tutaj rekurencją – funkcja menu wywołuje samą siebie.

    function article()
    {
        global $pdo;
        if(!empty($_GET))
        {
            $article_id = $_GET['page'];
            $rows = $pdo->query("SELECT * FROM artykuly WHERE id=$article_id;"); 
            foreach($rows as $r)
            {
                $article_content=$r;
            }
        }
return $article_content;
    
    }

Czas na funkcję article, nasze założenie jest takie, że każdy element naszego menu będzie w adresie URL przekazywał id artykułu, któremu odpowiada. Dlatego też sprawdzamy tu któremu z artykułów odpowiada dany identyfikator i przypisujemy go do zmiennej $article_content

    function url(){
        $url = $_SERVER["REQUEST_URI"];
        if(strpos($url,"/?"))
        {
        $pos = strrpos($url, "/?");
        return $path = substr($url,0,$pos);
        }
        else
        {
            return $path = $url;
        }
    }

Funkcja url() ma za zadanie zwrócić ścieżkę, do momentu gdy pojawia się w niej ‘/?’

    function display_menu()
    {
        $list = (menu(0));
        $path = url();
        echo "<ul>";
        foreach($list as $l)
        {
            echo "
            <li>
                <a href='".$path."/?page=".$l['id_artykulu']."'>".$l['nazwa']."</a>";

            foreach($l['children'] as $child )
            {
                echo "
                    <ul>
                        <li>
                            <a href='".$path."/?page=".$child['id_artykulu']."'>".$child['nazwa']."</a>
                        </li>
                    </ul>
            </li>";
            }
        }
        echo "
                    </ul>";
    }

Wreszcie funkcja display_menu(), zajmuje się wyświetleniem naszej struktury menu, wywołując przy okazji wcześniej stworzone funkcje i korzystając z ich wyników.

    function display_article($mode)
    {
        if(isset($_GET['page']))
        {
            $content=article();
            if($mode=="tresc")
            {
            return $content['tresc'];
            }
            if($mode="tytul")
            {
            return $content['tytul'];
            }
        }
    }


W końcu funkcja display_article(), pozwala na wyświetlenie tytułu, ale też treści konkretnego artykułu, przekazanego parametrem GET. Możemy te funkcje wywołać w trybie dla tytułu bądź też treści, bez sensu byłoby tworzenie dwóch osobnych funkcji do tego zadania.

Tak finalnie prezentuje się plik functions.php

<?php
require_once('db_connect.php');
    function menu($parent_id)
    {
        global $pdo;
        $list = [];
        $rows = $pdo->query("SELECT * FROM menu WHERE id_rodzica=$parent_id;");
        $i=0;
        foreach($rows as $r)
        {
            $list[$i]=$r;
            $list[$i]['children']=menu($r['id']);
            $i++;
        }
        return $list;
    }
    function article()
    {
        global $pdo;
        if(!empty($_GET))
        {
            $article_id = $_GET['page'];
            $rows = $pdo->query("SELECT * FROM artykuly WHERE id=$article_id;"); 
            foreach($rows as $r)
            {
                $article_content=$r;
            }
        }
return $article_content;
    
    }
    function url(){
        $url = $_SERVER["REQUEST_URI"];
        if(strpos($url,"/?"))
        {
        $pos = strrpos($url, "/?");
        return $path = substr($url,0,$pos);
        }
        else
        {
            return $path = $url;
        }
    }
    function display_menu()
    {
        $list = (menu(0));
        $path = url();
        echo "<ul>";
        foreach($list as $l)
        {
            echo "
            <li>
                <a href='".$path."/?page=".$l['id_artykulu']."'>".$l['nazwa']."</a>";

            foreach($l['children'] as $child )
            {
                echo "
                    <ul>
                        <li>
                            <a href='".$path."/?page=".$child['id_artykulu']."'>".$child['nazwa']."</a>
                        </li>
                    </ul>
            </li>";
            }
        }
        echo "
                    </ul>";
    }
    function display_article($mode)
    {
        if(isset($_GET['page']))
        {
            $content=article();
            if($mode=="tresc")
            {
            return $content['tresc'];
            }
            if($mode="tytul")
            {
            return $content['tytul'];
            }
        }
    }
?>


Nie pozostało nam nic innego jak dodać odpowiednie stylowanie w pliku styl.css

body{
    margin:0;
font-family: Consolas;
}
main{
display:flex;
background-color: #333333;
color:white;

}
nav{
    width: 15%;
}
a,h1{
    color:white;
text-decoration:none;
}
section{
    width: 85%;
    background: linear-gradient(to right, #750d01, #1f1c18);;
    padding-left:5%;
    padding-right:5%;
    min-height: 100vh;
}


Nie będę im poświęcał zbyt dużo uwagi, stylowanie jest tu kwestią drugorzędną. Każdy może nadać swojej pracy zupełnie dowolny wygląd.

I tak oto zakończyliśmy nasz mini-projekt. Myślę, że była to ciekawa lekcja dla osób, które poznają tajniki współpracy PHP i MySQL.