Ta strona używa ciasteczek (cookies), dzięki którym nasz serwis może działać lepiej. Dowiedz się więcej OK, rozumiem

[mysql/php] Jak wyświetlić dane z bazy (kategorie i zawartoś

Zdecydowałeś się na tworzenie interaktywnych stron przy pomocy PHP? Chcesz w łatwy i szybki sposób zarządzać danymi na swojej stronie? Tutaj znajdziesz pomoc w napotkanych przy tym trudnościach.

[mysql/php] Jak wyświetlić dane z bazy (kategorie i zawartoś

Post 05.05.2008, 01:02:42

Chciałem sobie zrobić dodawacz do plików pdf na stronę. Jako zielony chcący się naumieć strona jest poligonem doświadczalnym ale do rzeczy.

Mam w bazie kolumny "kategorie" i "pliki" (niektóre wpisy do pól nie są prawidłowe ale dałem je żeby coś było tymczasowo więc na to proszę uwagi nie zwracać).
Do kategorie jest zapisywana nazwa kategorii
do "pliki" nazwa pliku pdf, ścieżka, nazwa do linka, data dodania i wielkość pliku...

Chciałem wyświetlić wyniki w formie drzewa czyli

kategoria1
--------------------------------
tutaj odnośnik do pliku przypisanego do kategorii
---------
tutaj jeszcze jeden
---------
i jeszcze jeden
-------------------------------
Kategoria2


Nie mam też pewności co do prawidłowości struktury tabeli. W jaki sposób wyświetlić wyniki,bo na razie to albo wyświetlają mi się tylko kategorie albo kategoria pierwsza z przypisanymi jej plikami. Już wymiękam przy szukaniu rozwiązania i pomocy bo im więcej czytam tym bardziej się gubię.


Kod: Zaznacz cały
-- --------------------------------------------------------

--
-- Struktura tabeli dla  `kategorie`
--

CREATE TABLE `kategorie` (
  `id` int(4) NOT NULL auto_increment,
  `kategoria` char(50) NOT NULL,
  `podkategoria` char(50) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;

--
-- Zrzut danych tabeli `kategorie`
--

INSERT INTO `kategorie` VALUES(1, 'kategoria testowa1', '');
INSERT INTO `kategorie` VALUES(2, 'kategoria testowa2', '');
INSERT INTO `kategorie` VALUES(3, 'kategoria testowa3', '');
INSERT INTO `kategorie` VALUES(4, 'kategoria testowa', '');
INSERT INTO `kategorie` VALUES(5, 'kategoria testowa', '');
INSERT INTO `kategorie` VALUES(6, 'kategoria testowa', '');
INSERT INTO `kategorie` VALUES(7, 'kategoria testowa', '');
INSERT INTO `kategorie` VALUES(8, 'kategoria testowa', '');
INSERT INTO `kategorie` VALUES(9, 'kategoria testowa', '');

-- --------------------------------------------------------

--
-- Struktura tabeli dla  `pliki`
--

CREATE TABLE `pliki` (
  `id` int(4) NOT NULL auto_increment,
  `nazwa_pliku` char(30) NOT NULL,
  `nazwa_odnosnika` char(255) NOT NULL,
  `link` char(255) NOT NULL,
  `wielkosc_pliku` char(10) default NULL,
  `data_dodania` char(10) default NULL,
  `kategoria_id` char(50) default NULL,
  `podkategoria_id` char(50) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

--
-- Zrzut danych tabeli `pliki`
--

INSERT INTO `pliki` VALUES(1, 'umowa_odzielo.pdf', 'Jak wyliczyć dochód ze sprzedaży środka trwałego', 'Jak wyliczyć dochód ze sprzedaży środka trwałego', '', '', '1', '');
INSERT INTO `pliki` VALUES(2, 'jakis_plik.pdf', 'jakiś tekst dotyczący odnośnika', 'jakiś tekst dotyczący odnośnika', '', '', '1', '');
INSERT INTO `pliki` VALUES(3, 'kolejny_plik.pdf', 'jakiś tekst dotyczący odnośnika', 'jakiś tekst dotyczący odnośnika', '', '', '1', '');
Jazz
Posty: 93
Dołączył(a): 05.05.2002

Post 05.05.2008, 08:22:58

Szczerze mówiąc to Twoje tabele trochę mnie zdziwiły.
Nie mówiąc o dziwnych nazwach komórek i samych tabel, nawet nie potrafię zrozumieć do czego miałoby służyć coś takiego jak „podkategoria” (w tej postaci jakiej używasz).

Jakiś czas temu pisałem mały system artykułów, które można ułożyć hierarchicznie (kategorie i podkategorie, bez ogranicznia poziomu zagłębienia).

Dla Ciebie mogłoby to wyglądać mniej więcej tak:

Kategoria
Kod: Zaznacz cały
CREATE TABLE `Category` (
   `id`      INT   AUTO_INCREMENT,
   `ancestor`   INT    NOT NULL,
   `name`      VARCHAR(255)   NOT NULL,
   
      PRIMARY KEY (`id`)
)


Plik
Kod: Zaznacz cały
CREATE TABLE `File` (
   `id`      INT   AUTO_INCREMENT,
   `category`   INT    NOT NULL,
   `name`      VARCHAR(255)   NOT NULL,
   `uri`      VARCHAR(30)   NOT NULL,
   `addition`   TIMESTAMP   DEFAULT   CURRENT_TIMESTAMP,
   
      PRIMARY KEY (`id`)
)


Zasada działania kategorii i podkategorii.

Każda kategoria o `ancestor` z wartością „0” jest traktowana jako główna kategoria czyli jest wyświetlana na szczycie.
Chcąc więc pobrać główne kategorie należy wykonać:
Kod: Zaznacz cały
SELECT * FROM `Category` WHERE `ancestor` = '0'


Mając natomiast jakąś kategorię, powiedzmy z `id` równym „2”, żeby pobrać wszystkie podkategorie należące do tej właśnie kategorii trzeba wykonać:
Kod: Zaznacz cały
SELECT * FROM `Category` WHERE `ancestor` = '2'

Analogicznie, chcąc pobrać pliki należące do kategorii wykonuje się zapytanie:
Kod: Zaznacz cały
SELECT
   `F`.`id`   AS `file_id`,
   `F`.`name`   AS `file_name`,
   `F`.`uri`   AS `file_uri`,
   `F`.`addition`   AS `file_addition`,
   `C`.`id`   AS `category_id`,
   `C`.`name`   AS `category_name`,
   `C`.`ancestor`   AS `category_ancestor`
FROM
   `File` AS `F`
   `Category` AS `C`
WHERE
   `F`.`Category` = `C`.`id`
   AND
   `C`.`id` = 'IDENTYFIKATOR_KATEGORII'

Można oczywiście zapisać do bazy powyższe zapytanie jako widok („VIEW”), powiedzmy o nazwie „File_full” (ale bez ostatniego warunku).
Wtedy zapytanie do bazy miałoby postac:
Kod: Zaznacz cały
SELECT * FROM `File_full` WHERE `category_id` = 'IDENTYFIKATOR_KATEGORII'


Sprawa wyświetlania.

Mając taką hierarchię dobrze jest wykonać jej przeglądanie w ten sposób, że będąc w określonej kategorii widzi się tylko elementy do niej należące.
Można jednak wykonać również całościowy spis, czy to kategorii czy kategorii razem z plikami.

Zostawiam to jednak do rozwiązania Tobie, jako że już sporo napisałem. Jak znajdę czas to dopiszę.

P.S.
Staraj się dostosować typ komórki w bazie danych do wartości jaką w niej będziesz przetrzymywał.
Zapisując więc datę używaj DATE lub TIMESTAMP, a nie CHAR. Pozwoli Ci to np. na bardzo szybkie i wygodne sortowanie i wyszukiwanie danych ze względu na datę właśnie.
Nie wspominając już, że w postaci, którą podałem data dodania jest generowana przez bazę danych automatycznie.
Avatar użytkownikaqrakis
Romuald Kowalczyk

Posty: 85
Dołączył(a): 27.11.2005
Lokalizacja: Nowy Dwór Maz.

Post 05.05.2008, 19:57:45

Dzięki serdeczne za pomoc i wyjaśnienie. W sieci jest problem ze znalezieniem rzetelnych przykładów i zasad w praktyce stąd moje tabele i cała struktura tak bardzo chaotyczna i irracjonalna :) Niby się konsultowałem ze znajomym ale wychodzi na to również wie niewiele.

Przeanalizuję sobie Twoje przykłady i zrobię w oparciu o to tabele od nowa żeby mieć punkt wyjścia do dalszego kombinowania.
Jestem otwarty na ewentualne dalsze pomoce w tym temacie :)
Jazz
Posty: 93
Dołączył(a): 05.05.2002

Post 06.05.2008, 21:04:07

A skąd wiadomo, że na przykład chcąc uzyskać taką sytuację jak poniżej, czyli mamy 3 kategorie główne z ancestor '0' oraz podkategorie z ancestor '1', to podkategoria C i D będzie należeć do kategorii głównej 2 ?

kategoria główna 1
podkategoria A
podkategoria B

kategoria główna 2
podkategoria C
podkategoria D

kategoria główna 3
file1
file2
file3

Na razie mam taki kod w którym eksperymentuje, zapewne nie jest to właściwa forma i jest to dosyć prymitywne :) ale zarówno w PHP jak i MYSQL raczkuje. Spore problemy mam z zapytaniami, muszę znaleźć jakieś pomoce naukowe w sieci odnośnie zapytań.


Kod: Zaznacz cały
$q = mysql_query("SELECT * FROM category WHERE ancestor = '0'");
$q2 = mysql_query("SELECT * FROM category WHERE ancestor = '1'");
while($qq=mysql_fetch_array($q)){
echo $qq['name']."<br/>";
while($qq2=mysql_fetch_array($q2)){
echo $qq2['name']."<br/>";
}}


W tym momencie uzyskuje:

kategoria główna 1
podkategoria A
podkategoria B
kategoria główna 2
kategoria główna 3

A gdybym chciał przesunąć podkategorie A i B do kategorii głównej 2 ?
Jazz
Posty: 93
Dołączył(a): 05.05.2002

Post 06.05.2008, 21:13:07

Edit: Na końcu masz link do archiwum z wszystkimi plikami.

Zapiszę Ci jak powinien wyglądać kod tworzący taką strukturę jak opisałeś.
Całość opieram na klasach opisanych dalej (twoje pytanie pojawiło się gdy ja pisałem swój post).

Kod: Zaznacz cały
<?php
require_once('class/Categories.php');
require_once('class/Files.php');

// Utwórz rejestr
$registry = Registry_singleton::getInstance();
// Ustaw parametry połączenia do bazy danych
$registry->database = array(
      'host'      => 'localhost',
      'userName'   => 'root',
      'password'   => '',
      'databaseName'   => 'testA');


// Kategorie
$categories = new Categories();
// Pliki
$files = new Files();

// Kategorie
$arr = array(
   array(   'name' => 'Kategoria główna 1',      // 1
      'ancestor' => 0),
   array(   'name' => 'Kategoria główna 2',      // 2
      'ancestor' => 0),
   array(   'name' => 'Kategoria główna 3',      // 3
      'ancestor' => 0),
   array(   'name' => 'Podkategoria A',      // 4
      'ancestor' => 1),
   array(   'name' => 'Podkategoria B',   // 5
      'ancestor' => 1),
   array(   'name' => 'Podkategoria C',   // 6
      'ancestor' => 2),
   array(   'name' => 'Podkategoria D',   // 7
      'ancestor' => 2));

// Wpisz kategorie
foreach($arr as $category) {
   $categories->create($category);
}


// Pliki
$arr = array(
   array(   'name' => 'file1',   // 1
      'category' => 3,
      'uri' => '1.pdf'),
   array(   'name' => 'file2',   // 2
      'category' => 3,
      'uri' => '2.pdf'),
   array(   'name' => 'file3',   // 3
      'category' => 3,
      'uri' => '3.pdf'));

// Wpisz pliki
foreach($arr as $file) {
   $files->create($file);
}


A gdybym chciał przesunąć podkategorie A i B do kategorii głównej 2?

Musisz, przede wszystkim, znać identyfikator („id“) tej kategorii. W tym przypadku jest to „2”.
Musisz też znać „id” kategorii, które chcesz przenieść. Tutaj są to: „4“ i „5“.
Wykonujesz nast. kod:
Kod: Zaznacz cały
<?php
$categories->update(4, array('ancestor'=>2));
$categories->update(5, array('ancestor'=>2));

Można też dodać odpowiednie metody do klasy, ale w tej chwili są tylko te.
Chyba przyznasz, że prostsze to niż układanie zapytań?

KLASY

Przygotowałem Ci kilka klas, które znacznie ułatwią Ci pracę.
Ponadto pokazują pewien kierunek, w którym raczej warto iść pisząć jakiekolwiek aplikacje w PHP.

Dla Ciebie będą ważne konkretnie 2 klasy, „Categories” i „Files”, służące do prostych manipulacji na omawianych kategoriach i plikach.

W tej chwili nawet nie musisz wiedzieć jakie metody mają wszystkie klasy środowiska. Po prostu muszą być, żeby te, których będziesz używał działały.
Czyli dla Ciebie teraz ważne są tylko: „TablePrototype”, „Categories“ i „Files“.

Do działania te 2 klasy wymagają jeszcze 6 innych. Właściwie 3, ale bazuję na wcześniej napisanych klasach tylko dostosowanych do tego przykładu.

Ale najpierw baza danych.

Kod: Zaznacz cały
CREATE TABLE `Category` (
   `id`      INT   AUTO_INCREMENT,
   `ancestor`   INT   NOT NULL,
   `name`   VARCHAR(255)   NOT NULL,
   
   PRIMARY KEY (`id`)
)

Kod: Zaznacz cały
CREATE TABLE `File` (
   `id`      INT   AUTO_INCREMENT,
   `category`   INT      NOT NULL,
   `name`      VARCHAR(255)   NOT NULL,
   `uri`      VARCHAR(30)   NOT NULL,
   `addition`   TIMESTAMP   DEFAULT   CURRENT_TIMESTAMP,
   
   PRIMARY KEY (`id`)
)

Kod: Zaznacz cały
CREATE VIEW `Category_File` AS
SELECT
   `F`.`id`   AS `file_id`,
   `F`.`name`   AS `file_name`,
   `F`.`uri`   AS `file_uri`,
   `F`.`addition`   AS `file_addition`,
   `C`.`id`   AS `category_id`,
   `C`.`name`   AS `category_name`,
   `C`.`ancestor`   AS `category_ancestor`
FROM
   `File`      AS `F`,
   `Category`   AS `C`
WHERE
   `F`.`category` = `C`.`id`


Klasy podstawowe, które można zaliczyć jako środowisko aplikacji.

TransferObject.php
Kod: Zaznacz cały
<?php
class TransferObject {
   protected
      $arrVar = array();   // Przechowywane dane
   
   protected function testName( $name ) {
      return is_string($name);
   }
   
   protected function testValue( $value ) {
      return true;
   }
   
   protected function stripValue( $value ) {
      return $value;
   }
   
   public function __get( $name ) {
      return isset($this->arrVar[$name]) ? $this->arrVar[$name] : null;
   }
   
   public function __set( $name,  $value ) {
      if($this->testName($name) and $this->testValue($value)) {
         $this->arrVar[$name] = $this->stripValue($value);
         return true;
      }
      
      return false;
      
   }
   
   public function __isset( $name ) {
      return isset($this->arrVar[$name]);
   }
   
   public function __empty( $name ) {
      return empty($this->arrVar[$name]);
   }
   
   public function __unset( $name ) {
      unset($this->arrVar[$name]);
   }
   
}
?>

Registry.php
Kod: Zaznacz cały
<?php
require_once('TransferObject.php');

class Registry extends TransferObject {
   protected function testName( $name ) {
      return !$this->__isset($name);
   }
}
?>

Registry_singleton.php
Kod: Zaznacz cały
<?php
require_once('Registry.php');

/**
 * Registry as Singleton
 */
class Registry_singleton extends Registry {
   private static
      $instance;
   
   public static function getInstance() {
      if(!self::$instance instanceof self) {
         self::$instance = new self();
      }
      return self::$instance;
   }
}

EnvDatabase.php
Kod: Zaznacz cały
<?php
class EnvDatabase {
   const
      defaultResourceType = MYSQL_ASSOC;
      
   protected
      $database,
      $connected = false,
      $selected = false;
   
   function __construct( $params = null ) {
      if(is_array($params)) {
         $this->connect($params['host'], $params['userName'], $params['password']);
         
         if(!empty($params['databaseName'])) {
            $this->selectDatabase($params['databaseName']);            
         }
      }
      
      return $this->ready();
   }
   
   protected function stripQuery( $query ) {
      return $query;
   }
   
   public function connect( $host, $userName, $password ) {
      if($this->database = @mysql_connect($host, $userName, $password)) {
         $this->connected = true;
      }
      
      return $this->connected;
   }
   
   public function disconnect() {
      if(@mysql_close($this->database)) {
         $this->connected = false;
      }
      
      return $this->connected;
   }
   
   public function selectDatabase( $name ) {
      if(@mysql_select_db($name, $this->database)) {
         $this->selected = true;
      }
      
      return $this->selected;
   }
   
   public function connected() {
      return $this->connected;
   }
   
   public function ready() {
      return ($this->connected and $this->selected);
   }
   
   public function query( $query ) {
      return @mysql_query($this->stripQuery($query));
   }
   
   public function count( $resource ) {
      return @mysql_num_rows($resource);
   }
   
   public function fetch( $resource, $resourceType = null ) {
      if(is_null($resourceType)) {
         $resourceType = self::defaultResourceType;
      }
      
      return @mysql_fetch_array($resource, $resourceType);
   }
   
   public function fetchRow( $query, $resourceType = null ) {
      if(is_null($resourceType)) {
         $resourceType = self::defaultResourceType;
      }
      
      return @mysql_fetch_array(@mysql_query($this->stripQuery($query)), $resourceType);
   }
   
   public function fetchArray( $query, $resourceType = null ) {
      if(is_null($resourceType)) {
         $resourceType = self::defaultResourceType;
      }
      
      $resource = $this->query($this->stripQuery($query));
      
      while($row = $this->fetch($resource, $resourceType)) {
         $arr[] = $row;
      }
      
      return $arr;
   }
}
?>

EnvDatabase_singleton.php
Kod: Zaznacz cały
<?php
require_once('EnvDatabase.php');

/**
 * Database handler as Singleton
 */
class EnvDatabase_singleton extends EnvDatabase {
   private static
      $instance;
   
   public static function getInstance() {
      if(!self::$instance instanceof self) {
         $registry = Registry_singleton::getInstance();
         self::$instance = new self($registry->database);
      }
      return self::$instance;
   }
   
}


Czas na właściwe klasy.
Ze względy na zbieżność metod między obiema klasami dałem wspólną klasę-rodzica, która zajmuje się podstawami. Jest to nic innego jak klasa obsługi tabeli bazy danych (bardzo uproszczona), którą większość osób w końcu wymyśla. Występuje chociażby w Zend Framework.
TablePrototype.php
Kod: Zaznacz cały
<?php
require_once('Registry_singleton.php');
require_once('EnvDatabase_singleton.php');

abstract class TablePrototype {
   protected
      $name,
      $db;      // dostęp do bazy danych
   
   // Konstruktor
   function __construct() {
      // Pobierz instancję singletona EnvDatabase
      $this->db = EnvDatabase_singleton::getInstance();
   }
   
   public function setTable( $name ) {
      $this->name = $name;
   }

   // Wiersze z danym rodzicem
   public function withAncestor( $id ) {
      return $this->db->fetchArray(
      "SELECT * FROM `".$this->name."` WHERE `ancestor` = '$id'");
   }
   
   // Wiersz o danym ident.
   public function withId( $id ) {
      return $this->db->fetchRow(
      "SELECT * FROM `".$this->name."` WHERE `id` = '$id' LIMIT 1");
   }
   
   // Utwórz wiersz
   public function create( Array $fields ) {
      foreach($fields as $name => $value) {
         $names[] = "`$name`";
         $values[] = "'$value'";
      }
      
      $names = implode(',', $names);
      $values = implode(',', $values);
      
      return $this->db->query(
      "INSERT INTO `".$this->name."`
         ($names)
      VALUES
         ($values)");
   }
   
   // Zmień dane wiersza
   public function update( $id, Array $fields ) {
      foreach($fields as $name => $value) {
         $arr[] = "`$name` = '$value'";
      }
      
      $set = implode(',', $arr);
      
      return $this->db->query(
      "UPDATE
         `".$this->name."`
      SET
         $set
      WHERE
         `id` = '$id'
      LIMIT 1");
   }
   
   // Usuń wiersz
   public function delete( $id ) {
      return $this->db->query(
      "DELETE FROM `".$this->name."` WHERE `id` = '$id' LIMIT 1");
   }
}


I dwie klasy, których obiekty będą używane.
Categories.php
Kod: Zaznacz cały
<?php
require_once('Registry_singleton.php');
require_once('EnvDatabase_singleton.php');
require_once('TablePrototype.php');

/**
 * Categories
 */
class Categories extends TablePrototype {
   protected
      $name = 'Category';
   
   // Pliki należące do danej kategorii
   public function files( $categoryId ) {
      return $this->db->fetchArray(
      "SELECT * FROM `Category_File` WHERE `category_id` = '$categoryId'");
   }
}

Files.php
Kod: Zaznacz cały
<?php
require_once('Registry_singleton.php');
require_once('EnvDatabase_singleton.php');
require_once('TablePrototype.php');

/**
 * Files
 */
class Files extends TablePrototype {
   protected
      $name = 'File';
   
   // Pliki z danym rodzicem
   public function withAncestor( $id ) {
      return $this->db->fetchArray(
      "SELECT * FROM `".$this->name."` WHERE `category` = '$id'");
   }
}

Jak widać dzięki zastosowaniu prototypu tabeli klasy, które są esencją przykładu zostały zredukowane do minimum.

Można w tej chwili zadać sobie pytanie: „po co tyle zachodu”. Wystarczy spojrzeć na to w jaki sposób w tej chwili pobiera się np. wszystkie kategorie posiadające określonego rodzica.
Przykład wykorzystania
Kod: Zaznacz cały
<?php
require_once('class/Categories.php');
require_once('class/Files.php');

// Utwórz rejestr
$registry = Registry_singleton::getInstance();
// Ustaw parametry połączenia do bazy danych
$registry->database = array(
      'host'      => 'localhost',
      'userName'   => 'root',
      'password'   => '',
      'databaseName'   => 'testA');


// Kategorie
$categories = new Categories();
// Pliki
$files = new Files();


// Wykonaj zapytanie do kategorii
$arr = $categories->files(1);

// Wyświetl
echo '<pre>';
var_dump($arr);
echo '</pre>';

To wszystko. Tych kilka linijek, z których spora część to parametry połączenia z bazą danych, robią to co trzeba.
Takie podejście do sprawy jest bardzo wygodne.
Zwróć uwagę, że nawet nie wykonałem połączenia z bazą danych. Wystarczy podać parametry połączenia, a połączenie zostanie nawiązane automatycznie.
Co do samych klas to przydałoby się oczywiście jeszcze wbudowanie np. stronicowania, ale to już szczegóły.

Żebyś lepiej zrozumiał sposób użycia klas dam Ci jeszcze kod do utworzenia tabel w bazie danych oraz ich wypełnienia danymi.

Utwórz tabele
Kod: Zaznacz cały
<?php
// Obsługa bazy danych
$db = EnvDatabase_singleton::getInstance();

// Utwórz `Category`
$db->query(
"CREATE TABLE `Category` (
   `id`      INT   AUTO_INCREMENT,
   `ancestor`   INT   NOT NULL,
   `name`   VARCHAR(255)   NOT NULL,
   
   PRIMARY KEY (`id`)
)");

// Utwórz `File`
$db->query(
"CREATE TABLE `File` (
   `id`      INT   AUTO_INCREMENT,
   `category`   INT      NOT NULL,
   `name`      VARCHAR(255)   NOT NULL,
   `uri`      VARCHAR(30)   NOT NULL,
   `addition`   TIMESTAMP   DEFAULT   CURRENT_TIMESTAMP,
   
   PRIMARY KEY (`id`)
)");

// Utwórz `Category_File`
$db->query(
"CREATE VIEW `Category_File` AS
SELECT
   `F`.`id`   AS `file_id`,
   `F`.`name`   AS `file_name`,
   `F`.`uri`   AS `file_uri`,
   `F`.`addition`   AS `file_addition`,
   `C`.`id`   AS `category_id`,
   `C`.`name`   AS `category_name`,
   `C`.`ancestor`   AS `category_ancestor`
FROM
   `File`      AS `F`,
   `Category`   AS `C`
WHERE
   `F`.`category` = `C`.`id`");

Zwróc uwagę na sposób utworzenia obiektu obsługującego bazę danych. Jest to „Singleton” (jak nie wiesz czym jest, to bez problemu znajdziesz na google), więc pobierana jest tylko jego instancja. Połączenie z bazą danych jest nawiązywane w momencie pierwszego wywołania instancji obiektu. Pod warunkiem, że w rejestrze znajduje się wpis „database” określający parametry połączenia.
O rejestrze też znajdziesz wiadomości w internecie. „Register” i „Singleton” to wzorce projektowe („Design Patterns”).

Wpisz dane
Kod: Zaznacz cały
<?php
// Kategorie
$arr = array(
   array(   'name' => 'A',      // 1
      'ancestor' => 0),
   array(   'name' => 'B',      // 2
      'ancestor' => 0),
   array(   'name' => 'C',      // 3
      'ancestor' => 0),
   array(   'name' => 'D',      // 4
      'ancestor' => 0),
   array(   'name' => 'A-a',   // 5
      'ancestor' => 1),
   array(   'name' => 'A-b',   // 6
      'ancestor' => 1),
   array(   'name' => 'B-a',   // 7
      'ancestor' => 2),
   array(   'name' => 'C-a',   // 8
      'ancestor' => 3),
   array(   'name' => 'C-b',   // 9
      'ancestor' => 4));

// Wpisz kategorie
foreach($arr as $category) {
   $categories->create($category);
}


// Pliki
$arr = array(
   array(   'name' => 'P_A',   // 1
      'category' => 1,
      'uri' => 'plik/1'),
   array(   'name' => 'P_A-a',   // 2
      'category' => 5,
      'uri' => 'plik/2'),
   array(   'name' => 'P_B',   // 3
      'category' => 2,
      'uri' => 'plik/3'),
   array(   'name' => 'P_C',   // 4
      'category' => 3,
      'uri' => 'plik/4'));

// Wpisz pliki
foreach($arr as $file) {
   $files->create($file);
}


Oczywiście oba kody, trzeba wpisać do „Przykładu” po linii z parametrami połączenia.
Jak widać samo dodanie danych do tabeli to 2 linijki kodu.

Podsumowanie
Dwie napisane dla tego przykładu klasy: „Categories”, „Files” tylko ułatwiają pracę. Jak już pewne zauważyłeś nie prezentuję tutaj mechanizmu interakcji, czyli jak należy użyć danych, żeby użytkownik mógł się poruszać po kategoriach.

Nie wiem, czy znasz chociaz podstawy programowania obiektowego. Jeśli nie to podpowiem Ci, że obie te klasy posiadają również metody (funkcje) zdefiniowane w „TablePrototype”, ponieważ na tym polega dziedziczenie;

P.S.
W żadnym wypadku nie mogę się zgodzić, że w internecie jest mało materiałów dot. PHP i MySQL. Sam tylko z internetu się uczę (nawet nie zadaję za bardzo pytań na forach, bo wolę sam rozwiązywać problemy). Trzeba po prostu dobrze szukać, a także nie ograniczać się do manuali i tutoriali. Trzeba czytać również artykuły (chociaż głównie są po angielsku), bo to w nich omawiane są techniki dobrego programowania i cała teoria programowania.

P.P.S
Przygotowałem archiwum z wszystkimi plikami. Wystarczy wrzucić na serwer. Ustawić parametry bazy danych (w „init.php”).
http://qrakis.50webs.com/php/archive/examples/example-0.zip




edit
Interfejs

Napisałem jeszcze kontroler, czyli stronę, która pozwala na poruszanie się po kategoriach i wyświetla podkategorie i pliki w nich zawarte.

Wystarczy wrzucić do folderu z poprzednim przykładem (tym z archiwum „zip”) plik o nazwie „front.php“ i zawartości:
Kod: Zaznacz cały
<?php
echo
'<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>';


include('init.php');

// Kategorie
$categories = new Categories();
// Pliki
$files = new Files();


// Ustawienie id. aktualnej kategorii
if(isset($_GET['category'])) {
   $intCategory = (int) $_GET['category'];
}
else {
   $intCategory = 0;
}

// Pobranie informacji o aktualnej kategorii
$arrActual = $categories->withId($intCategory);


// Jeśli kategoria jest różna od głównej to trzeba sprawdzić czy istnieje
if(!is_array($arrActual) and ($intCategory != 0)) {
   echo '<p>Nie ma takiej kategorii.</p>'
   . '<p><a href="front.php">Główna kategoria</a></p>';
}
else {
   // Wypisz nazwę aktualnej kategorii
   if($intCategory == 0) {
      echo '<h1>Kategoria główna</h1>';
   }
   else {
      echo '<h1>'.$arrActual['name'].'</h1>';
   }

   // Jeśli kat. różna od głównej to trzeba zrobić link „Wróć“
   if($intCategory != 0) {
      echo '<p><a href="front.php?category='.$arrActual['ancestor'].'">Wróć</a></p>';
   }
   
   // Kat. należące do aktualnej kat.
   $arrCategories = $categories->withAncestor($intCategory);
   // Pliki należące do aktualnej kat.
   $arrFiles = $files->withAncestor($intCategory);
   
   
   echo '<h2>Kategorie</h2>';
   // Wypisz kategorie
   if(is_array($arrCategories)) {
      echo '<ol>';
      foreach($arrCategories as $tmpCategory) {
         echo '<li><a href="front.php?category='.$tmpCategory['id'].'">'.$tmpCategory['name'].'</a></li>';
      }
      echo '</ol>';
   }
   else {
      echo '<p>Brak kategorii.</p>';
   }
   
   echo '<h2>Pliki</h2>';
   // Wypisz pliki
   if(is_array($arrFiles)) {
      echo '<ol>';
      foreach($arrFiles as $tmpFile) {
         echo '<li><a href="'.$tmpFile['uri'].'">'.$tmpFile['name'].'</a></li>';
      }
      echo '</ol>';
   }
   else {
      echo '<p>Brak plików.</p>';
   }
}

echo '</body></html>';
?>

Nie jest to ani zbyt długie, ani skomplikowane. Dzięki zastosowaniu obiektów odpowiednich klas łatwiej jest pisać aplikację, a jej kod staje się bardziej czytelny.
Avatar użytkownikaqrakis
Romuald Kowalczyk

Posty: 85
Dołączył(a): 27.11.2005
Lokalizacja: Nowy Dwór Maz.

Post 07.05.2008, 21:57:13

qrakis niesamowicie dziękuję za twój czas, to co zrobiłeś jest obłędne. Nie spodziewałem się, że komuś w ogóle będzie chciało sie poświecić energię i czas na tłumaczenie i jeszcze udostępnienie funkcjonalnych przykładów. Dużo wiedzy mi brakuje, przy części tego co napisałeś robiłem wielkie oczy ale mam punkt wyjścia do zrozumienia, do czego przyczyniło się fajne objaśnienie z twojej strony zagadnienia.

Tak na przyszłość chętnie utrzymał bym kontakt w przypadku gdybym miał jakiś projekt do wykonania oczywiście odpłatnie :)
Jazz
Posty: 93
Dołączył(a): 05.05.2002

Post 07.05.2008, 22:17:55

Spoko. Sam też musiałem się nad tym zastanowić, ale dobrze, że doceniasz mój czas, bo jednak trochę trwało napisanie tego.
Na pewno będziesz musiał trochę poczytać, żeby to zrozumieć skoro dopiero zaczynasz.
Avatar użytkownikaqrakis
Romuald Kowalczyk

Posty: 85
Dołączył(a): 27.11.2005
Lokalizacja: Nowy Dwór Maz.

Post 09.05.2008, 03:05:45

Analizuję sobie tą "czarną magię" :), nie wiem w jaki sposób zmienić żeby przy danej kategorii i podkategorii napisane było ile w środku znajduje się plików. Czyli przed wejściem do danej kategorii można się dowiedzieć czego się mniej więcej spodziewać.

Oraz na samej górze wyświetlić ścieżkę w postaci linków w stylu:

Kategoria1->podkategoria1->podkategoria_A

Ukazujące gdzie w danym momencie się znajdujemy.

Czy w oparciu o ten sam mechanizm tworzenia robi się dodawanie/edycję/kasowanie wpisów do bazy?
Bo o ile oczy mnie nie mylą to w pliku TablePrototype.php są funkcje odpowiedzialne za te operacje?
Jazz
Posty: 93
Dołączył(a): 05.05.2002

Post 09.05.2008, 11:19:15

Wyświetlanie liczby zawartych plików:
Wystarczy jak pobierzesz listę plików (Array) i policzysz funkcją count. Oczywiście możesz też dopisać funkcję robiącą to w zapytaniu.

Wyświetlanie ścieżki:
Z tym jest trochę trudniej, bo trzeba użyć rekurencji. Zaczynasz od kategorii najwyższej i po kolei pobierasz (rekurencyjnie) jej przodków, aż do osiągnięcia głównego (z indeksem 0).

Dodawanie/edycja/kasowanie itp.:
Tak. Wszystkie funkcje z „TablePrototype” są dziedziczone przez „Category” i „File”. Inna sprawa, że niektóre są nadpisane (tj. funkcja „withAncestor” w „File”).
Avatar użytkownikaqrakis
Romuald Kowalczyk

Posty: 85
Dołączył(a): 27.11.2005
Lokalizacja: Nowy Dwór Maz.


Powrót do PHP i bazy danych


 


  • Podobne wątki
    Odpowiedzi
    Wyświetlone
    Ostatni post

Kto przegląda forum

Użytkownicy przeglądający ten dział: Google Adsense [Bot] i 6 gości

Hosting, Domeny, SSL

Subskrypcja

Mamy 51973 zarejestrowanych użytkowników.
Najnowszy użytkownik: Eddierob


Nasi użytkownicy napisali:

  • 938345 wiadomości
  • w 247865 tematach

Najnowsze wpisy na blogu

Najnowsze artykuły

Najaktywniejsi (ostatnie 30 dni)


cron