logo

Dodatni tipovi blokova (EBT) - Novo iskustvo rada sa Layout Builder-om❗

Dodatni tipovi blokova (EBT) – stilizovani, prilagodljivi tipovi blokova: slajdšouvi, kartice sa tabovima, kartice, akordeoni i mnogi drugi. Ugrađena podešavanja za pozadinu, DOM Box, javascript dodatke. Iskusite budućnost kreiranja rasporeda već danas.

Demo EBT moduli Preuzmite EBT module

❗Dodatni tipovi pasusa (EPT) – Novo iskustvo rada sa pasusima

Dodatni tipovi pasusa (EPT) – analogni skup modula zasnovan na pasusima.

Demo EPT moduli Preuzmite EPT module

Scroll

1.5. Povezivanje klasa za rad sa bazom podataka i šablonima

26/05/2025, by Ivan

Kreirali smo strukturu za naš framework, sada je vreme da razmislimo o čuvanju podataka: vesti, proizvoda. Objekat za rad sa bazom podataka treba da može:

  • Upravljati konekcijama sa bazom
  • Obezbediti malu apstrakciju od baze podataka
  • Keširati upite
  • Pojednostaviti uobičajene operacije sa bazom

Za to ćemo napraviti objekat Registry/objects/db.class.php:

<?php

/**
 * Upravljanje bazom podataka
 * Pruža malu apstrakciju baze podataka
 */
class db {

  /**
   * Omogućava višestruke konekcije sa bazom
   * retko se koristi ali može biti korisno
   */
  private $connections = array();

  /**
   * Aktivna konekcija
   * setActiveConnection($id) menja aktivnu konekciju
   */
  private $activeConnection = 0;

  /**
   * Keširani izvršeni upiti
   */
  private $queryCache = array();

  /**
   * Keširani podaci koji su dobijeni
   */
  private $dataCache = array();

  /**
   * Poslednji upit
   */
  private $last;

  /**
   * Konstruktor
   */
  public function __construct()
  {
  }

  /**
   * Kreira novu konekciju
   * @param String hostname baze
   * @param String korisničko ime baze
   * @param String lozinka baze
   * @param String ime baze
   * @return int ID nove konekcije
   */
  public function newConnection( $host, $user, $password, $database )
  {
    $this->connections[] = new mysqli( $host, $user, $password, $database );
    $connection_id = count( $this->connections )-1;
    if( mysqli_connect_errno() )
    {
      trigger_error('Greška pri povezivanju na host. '.$this->connections[$connection_id]->error, E_USER_ERROR);
    }

    return $connection_id;
  }

  /**
   * Zatvara aktivnu konekciju
   */
  public function closeConnection()
  {
    $this->connections[$this->activeConnection]->close();
  }

  /**
   * Menja aktivnu konekciju
   * @param int ID nove konekcije
   */
  public function setActiveConnection( int $new )
  {
    $this->activeConnection = $new;
  }

  /**
   * Kešira upit
   * @param String upit
   * @return int pokazivač na keširani upit
   */
  public function cacheQuery( $queryStr )
  {
    if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) )
    {
      trigger_error('Greška pri izvršavanju i keširanju upita: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR);
      return -1;
    }
    else
    {
      $this->queryCache[] = $result;
      return count($this->queryCache)-1;
    }
  }

  /**
   * Vraća broj redova iz keša
   * @param int pokazivač keša upita
   * @return int broj redova
   */
  public function numRowsFromCache( $cache_id )
  {
    return $this->queryCache[$cache_id]->num_rows;
  }

  /**
   * Vraća rezultate iz keša
   * @param int pokazivač keša upita
   * @return array redovi rezultata
   */
  public function resultsFromCache( $cache_id )
  {
    return $this->queryCache[$cache_id]->fetch_array(MYSQLI_ASSOC);
  }

  /**
   * Kešira podatke
   * @param array podaci
   * @return int pokazivač na keširane podatke
   */
  public function cacheData( $data )
  {
    $this->dataCache[] = $data;
    return count( $this->dataCache )-1;
  }

  /**
   * Vraća podatke iz keša
   * @param int pokazivač keša podataka
   * @return array podaci
   */
  public function dataFromCache( $cache_id )
  {
    return $this->dataCache[$cache_id];
  }

  /**
   * Briše zapise iz tabele
   * @param String ime tabele
   * @param String uslov za brisanje
   * @param int limit brisanih redova
   */
  public function deleteRecords( $table, $condition, $limit )
  {
    $limit = ( $limit == '' ) ? '' : ' LIMIT ' . $limit;
    $delete = "DELETE FROM {$table} WHERE {$condition} {$limit}";
    $this->executeQuery( $delete );
  }

  /**
   * Ažurira zapise u tabeli
   * @param String ime tabele
   * @param array izmene polje => vrednost
   * @param String uslov
   * @return bool
   */
  public function updateRecords( $table, $changes, $condition )
  {
    $update = "UPDATE " . $table . " SET ";
    foreach( $changes as $field => $value )
    {
      $update .= "`" . $field . "`='{$value}',";
    }
    $update = substr($update, 0, -1);
    if( $condition != '' )
    {
      $update .= " WHERE " . $condition;
    }
    $this->executeQuery( $update );
    return true;
  }

  /**
   * Ubacuje zapis u tabelu
   * @param String ime tabele
   * @param array podaci polje => vrednost
   * @return bool
   */
  public function insertRecords( $table, $data )
  {
    $fields  = "";
    $values = "";

    foreach ($data as $f => $v)
    {
      $fields  .= "`$f`,";
      $values .= ( is_numeric( $v ) && ( intval( $v ) == $v ) ) ? $v."," : "'$v',";
    }
    $fields = substr($fields, 0, -1);
    $values = substr($values, 0, -1);

    $insert = "INSERT INTO $table ({$fields}) VALUES({$values})";
    $this->executeQuery( $insert );
    return true;
  }

  /**
   * Izvršava upit
   * @param String upit
   */
  public function executeQuery( $queryStr )
  {
    if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) )
    {
      trigger_error('Greška pri izvršavanju upita: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR);
    }
    else
    {
      $this->last = $result;
    }
  }

  /**
   * Vraća redove poslednjeg upita
   * @return array
   */
  public function getRows()
  {
    return $this->last->fetch_array(MYSQLI_ASSOC);
  }

  /**
   * Broj pogođenih redova poslednjim upitom
   * @return int
   */
  public function affectedRows()
  {
    return $this->connections[$this->activeConnection]->affected_rows;
  }

  /**
   * Sanitizuje podatke
   * @param String podaci za sanitizaciju
   * @return String očišćeni podaci
   */
  public function sanitizeData( $data )
  {
    return $this->connections[$this->activeConnection]->real_escape_string( $data );
  }

  /**
   * Destruktor, zatvara konekcije
   */
  public function __destruct()
  {
    foreach( $this->connections as $connection )
    {
      $connection->close();
    }
  }
}
?>

Pre nego što krenemo na konekciju sa bazom, pogledajmo šta naš klasa radi. Možemo raditi jednostavne operacije ubacivanja, ažuriranja, brisanja preko metoda klase:

// Ubacivanje
$registry->getObject('db')->insertRecords( 'products', array('name'=>'Šolja' ) );
// Ažuriranje
$registry->getObject('db')->updateRecords( 'products', array('name'=>'Crvena šolja' ), 'ID=2' );
// Brisanje
$registry->getObject('db')->deleteRecords( 'products', "name='Crvena šolja'", 5 );

Klasa takođe podržava keširanje.

Sada ćemo dodati još jedan objekat za upravljanje šablonima Registry/objects/template.class.php

<?php

if ( ! defined( 'FW' ) )
{
  echo 'Ovaj fajl se može pozvati samo iz index.php, ne direktno';
  exit();
}

/**
 * Klasa za rad sa šablonima
 */
class template {

  private $page;

  /**
   * Konstruktor
   */
  public function __construct()
  {
    include( APP_PATH . '/Registry/objects/page.class.php');
    $this->page = new Page();
  }

  /**
   * Dodaje šablon u stranicu
   * @param String $tag oznaka u sadržaju, npr. {hello}
   * @param String $bit putanja do šablona
   */
  public function addTemplateBit( $tag, $bit )
  {
    if( strpos( $bit, 'Views/' ) === false )
    {
      $bit = 'Views/Templates/' . $bit;
    }
    $this->page->addTemplateBit( $tag, $bit );
  }

  /**
   * Uklanja i zamenjuje oznake šablona u sadržaju
   */
  private function replaceBits()
  {
    $bits = $this->page->getBits();
    foreach( $bits as $tag => $template )
    {
      $templateContent = file_get_contents( $template );
      $newContent = str_replace( '{' . $tag . '}', $templateContent, $this->page->getContent() );
      $this->page->setContent( $newContent );
    }
  }

  /**
   * Zamenjuje oznake u sadržaju sa podacima ili kešom
   */
  private function replaceTags()
  {
    $tags = $this->page->getTags();
    foreach( $tags as $tag => $data )
    {
      if( is_array( $data ) )
      {
        if( $data[0] == 'SQL' )
        {
          $this->replaceDBTags( $tag, $data[1] );
        }
        elseif( $data[0] == 'DATA' )
        {
          $this->replaceDataTags( $tag, $data[1] );
        }
      }
      else
      {
        $newContent = str_replace( '{' . $tag . '}', $data, $this->page->getContent() );
        $this->page->setContent( $newContent );
      }
    }
  }

  /**
   * Zamenjuje oznake podacima iz baze
   */
  private function replaceDBTags( $tag, $cacheId )
  {
    $block = '';
    $blockOld = $this->page->getBlock( $tag );

    while ($tags = Registry::getObject('db')->resultsFromCache( $cacheId ) )
    {
      $blockNew = $blockOld;
      foreach ($tags as $ntag => $data)
      {
        $blockNew = str_replace("{" . $ntag . "}", $data, $blockNew);
      }
      $block .= $blockNew;
    }
    $pageContent = $this->page->getContent();
    $newContent = str_replace( '' . $blockOld . '', $block, $pageContent );
    $this->page->setContent( $newContent );
  }

  /**
   * Zamenjuje oznake podacima iz keša
   */
  private function replaceDataTags( $tag, $cacheId )
  {
    $block = $this->page->getBlock( $tag );
    $blockOld = $block;
    while ($tags = Registry::getObject('db')->dataFromCache( $cacheId ) )
    {
      foreach ($tags as $tag => $data)
      {
        $blockNew = $blockOld;
        $blockNew = str_replace("{" . $tag . "}", $data, $blockNew);
      }
      $block .= $blockNew;
    }
    $pageContent = $this->page->getContent();
    $newContent = str_replace( $blockOld, $block, $pageContent );
    $this->page->setContent( $newContent );
  }

  /**
   * Vraća objekat stranice
   */
  public function getPage()
  {
    return $this->page;
  }

  /**
   * Gradi sadržaj stranice iz šablona
   */
  public function buildFromTemplates()
  {
    $bits = func_get_args();
    $content = "";
    foreach( $bits as $bit )
    {
      if( strpos( $bit, 'skins/' ) === false )
      {
        $bit = 'Views/Templates/' . $bit;
      }
      if( file_exists( $bit ) == true )
      {
        $content .= file_get_contents( $bit );
      }
    }
    $this->page->setContent( $content );
  }

  /**
   * Pretvara niz podataka u oznake
   */
  public function dataToTags( $data, $prefix )
  {
    foreach( $data as $key => $content )
    {
      $this->page->addTag( $key.$prefix, $content);
    }
  }

  public function parseTitle()
  {
    $newContent = str_replace('', '<title>'. $this->page->getTitle(), $this->page->getContent() );
    $this->page->setContent( $newContent );
  }

  /**
   * Parzira i prikazuje sadržaj
   */
  public function parseOutput()
  {
    $this->replaceBits();
    $this->replaceTags();
    $this->parseTitle();
  }

}
?>
</pre>

<p>Definisali smo i klasu Page Registry/objects/page.class.php:</p>

<pre class="1;">
<?php

/**
 * Naša klasa za stranicu
 * Omogućava dodatne funkcije kao što su zaštićene stranice, dodavanje js/css itd.
 */
class page {

  private $css = array();
  private $js = array();
  private $bodyTag = '';
  private $bodyTagInsert = '';

  private $authorised = true;
  private $password = '';

  private $title = '';
  private $tags = array();
  private $postParseTags = array();
  private $bits = array();
  private $content = "";

  function __construct() { }

  public function getTitle()
  {
    return $this->title;
  }

  public function setPassword( $password )
  {
    $this->password = $password;
  }

  public function setTitle( $title )
  {
    $this->title = $title;
  }

  public function setContent( $content )
  {
    $this->content = $content;
  }

  public function addTag( $key, $data )
  {
    $this->tags[$key] = $data;
  }

  public function getTags()
  {
    return $this->tags;
  }

  public function addPPTag( $key, $data )
  {
    $this->postParseTags[$key] = $data;
  }

  public function getPPTags()
  {
    return $this->postParseTags;
  }

  public function addTemplateBit( $tag, $bit )
  {
    $this->bits[ $tag ] = $bit;
  }

  public function getBits()
  {
    return $this->bits;
  }

  public function getBlock( $tag )
  {
    preg_match ('#<!-- START '. $tag . ' -->(.+?)<!-- END '. $tag . ' -->#si', $this->content, $tor);

    $tor = str_replace ('<!-- START '. $tag . ' -->', "", $tor[0]);
    $tor = str_replace ('<!-- END '  . $tag . ' -->', "", $tor);

    return $tor;
  }

  public function getContent()
  {
    return $this->content;
  }

}
?>
</pre>

<p>Sada kada smo kreirali klase za rad sa bazom i šablonima, povežimo ih.</p>

<p>Dodajmo metodu <code>storeCoreObjects()</code> u Registry/registry.class.php:</p>

<pre class="1;">
    public function storeCoreObjects()
    {
      $this->storeObject('db', 'db' );
      $this->storeObject('template', 'template' );
    }
</pre>

<p>U njoj ćemo definisati koje klase povezujemo.</p>

<p>Takođe napravimo tabelu users sa poljima id, name, email. SQL fajl sa primerom baze biće dodat na GitHub.</p>

<p>Sada kreirajmo šablon Views/Templates/main.tpl.php za prikaz glavne stranice:</p>

<pre class="1;">
<html>
<head>
    <title>Powered by PCA Framework</title>
</head>
<body>
<h1>Naši korisnici</h1>
<p>Ispod je spisak naših korisnika:</p>
<ul>
<!-- START users -->
<li>{name} {email}</li>
<!-- END users -->
</ul>
</body>
</html>
</pre>

<p>U index.php povežimo bazu i šablon:</p>

<pre class="1;">
<?php
session_start();

error_reporting(E_ALL);

define( "APP_PATH", dirname( __FILE__ ) ."/" );
define( "FW", true );

function __autoload( $class_name )
{
    require_once('Controllers/' . $class_name . '/' . $class_name . '.php' );
}

require_once('Registry/registry.class.php');
$registry = Registry::singleton();

$registry->storeCoreObjects();

$registry->getObject('db')->newConnection('localhost', 'root', '', 'framework');

$registry->getObject('template')->buildFromTemplates('main.tpl.php');

$cache = $registry->getObject('db')->cacheQuery('SELECT * FROM users');

$registry->getObject('template')->getPage()->addTag('users', array('SQL', $cache) );

$registry->getObject('template')->getPage()->setTitle('Naši korisnici');

$registry->getObject('template')->parseOutput();
print $registry->getObject('template')->getPage()->getContent();

print $registry->getFrameworkName();

exit();
?>
</pre>

<p>Ako je sve ispravno i baza ima korisnike, biće prikazan spisak korisnika.</p>

<p>U slučaju grešaka proverite kod na GitHubu jer je moguće da su neke ispravke u prethodnim člancima potrebne.</p>