HOME . Downloads
Excellent code to learn object oriented programming, model-view-controller code separation (structured programming), single page application (4 utility-helper functions + 5 CRUD functions), all one selfjoin table CRUD code in one script, require scripts, not URL them, most code outside Apache doc root, Javascript msg & yes-no dialogs and key TAB->ENTER.
(see Home -> site_ver2.rar).
6.Dec.2015 code refactored and parialy improved. Later (next year) I shall finish all I planed and upload also site_ver2.rar where is code outside appl. root. Below is newest code, other is in
Home -> site_ver2.rar
2.Sept.2015 Download – see site_ver2.rar from my article 9. Code below is enough to understand – it is not much changed.
1. Download sqlitestudio and install DDL at end tema.php
2. 1_sync_tema_sifrarnik_JtoH.bat
rem SINCHRONIZATION: 2 click this .bat script (or Git) rem J:\awww\apl\dev1\01apl\04tema\1_sync_tema_sifrarnik_JtoH.bat rem rem robocopy <Original Folder> <Destination Folder> /e /purge rem **** OUTPUTS EG : ***** rem Total Copied Skipped Mismatch FAILED Extras rem Dirs : 20 0 20 0 0 0 rem Files : 94 6 88 0 0 0 rem Bytes : 453.9 k 36.6 k 417.2 k 0 0 0 rem Times : 0:00:00 0:00:00 0:00:00 0:00:00 rem rem Speed : 708811 Bytes/sec. 40.558 MegaBytes/min. rem Ended : 6. prosinca 2015. 20:33:19 rem 1. resources outside appl. tree - utils, settings, css, img rem 1.1 robocopy J:\awww\apl\dev1\config_site.php H:\awww\apl\dev1\config_site.php /e /purge rem 1.2 robocopy J:\awww\apl\zinc H:\awww\apl\zinc /e /purge rem 2. inside appl. tree robocopy J:\awww\apl\dev1\01apl\04tema H:\awww\apl\dev1\01apl\04tema /e /purge pause
3. tema.php Code – all in one script ~350 lines :
<?php /** * Themes (threads) (msg-reply, task, menu) * Teme (niti) (poruka-odgovor, zadaća, izbornik) * PHP, Javascript, PDO SQLite3, selfjoin, 2015.12.7 * * LICENCE: Free code example - if you use it, do not remove this: * Slavko Srakočić, Croatia, Zagreb * see my blog http://phporacle.altervista.org * * * 1. ADRESSES : * J:\awww\apl\dev1\01apl\04tema\tema.php * http://dev1:8083/01apl/04tema/tema.php * * 2. robocopy (or Git) SYNCHRONIZATION: 2 click this .bat script (or git) * J:\awww\apl\dev1\01apl\04tema\1_sync_tema_sifrarnik_JtoH.bat * * 3. DDL: see CREATE TABLE message at this script end * * 4. Home page looks like : * Open new thema (nit, thread) Help = Klik Ord.Nr * |...| = |bytes| idniti, rbruniti, nivo, id, idviši | * 1. MSGs self join sqlite3 THIS PG by ss @ 2015-03-25 01:41 |DELETE| 1900 |UPDATE |1,0,0 ,1,0| * 1. funkcije by ss @ 2015-03-25 01:42 |DEL| 1654 |UPD|1,1,1 ,2,1| * 2. Re: funkcije by ss @ 2015-03-26 14:31 |DEL| 14 |UPD|1,2,2 ,18,2| * 3. funkcija save() by ss @ 2015-03-25 01:52 |DEL| 1296 |UPD|1,3,2 ,3,2| * ... * 2. Z-WAMP menu by aa @ 2015-12-05 15:58 |DELE| 14 |UPD |3,0,0 ,25,0| * * * 5. PHP CREATED JAVASCRIPT MSG & YES-NO DIALOGS : * $this->utl->phpjsmsg(7 parameters), phpjsmsgyn() * 6. F U N C T I O N S IN THIS SCRIPT *LINE FUNCTION WHAT (HOW) IS DOING *89: public __construct() { // INITIALIZE (B O O T S T R A P I N G) *151: public ctr() { // M A N A G E (R O U T I N G) *192: protected crerec() { // crud1. FRONTEND A D D SELFJOIN ROW *257: protected tbl() { // crud2. D I S P L A Y HIERARCHY *363: protected frm_rpt() { // crud3. R O W REPORT (m s g b o d y...) *417: protected frm_post() // crud4. ENTER USER DATA & PROCESS IT *491: protected delrec() { // crud5 BACKEND D E L E T E R O W *522: protected valid() { *538: protected logAndDie(Exception $e) { * */ use utlmoj\utlmoj as utl; ini_set('max_execution_time', 0); $start = microtime(true); $selfj = new SelfJoin(); // should be named: MessageBoard, MainMenu... $selfj->ctr(); //ROUTING = starts scripts according url params cmd, ... $end = microtime(true); echo '<br />'.'Vrijeme izvođenja: '.($end - $start) . ' sekundi'; class SelfJoin { // should be named: MessageBoard, MainMenu... // 1. C R U D (M O D E L) PROPERTIES : protected $inTransaction = false; protected $db; protected $table; protected $idname; protected $idval; // c r u d actions (CURSORS = named sql set process comands): // D = cursor for deleting protected $cdel_row_byid ; // R e a d cursor protected $cr_max_rbrdet_inm ; protected $cr_byid_nivo_orddet_inm ; protected $cr_byid ; // R E A D r o w B Y I D (M O D E L c R u d) protected $cr_byid_colname ; // U p d a t e cursor protected $cu_incr_rbrdet_inm ; // C r e a t e cursor protected $cc_mast_or_det ; // 2. U T L S F U N C T I O N S (H E L P E R S) : protected $utl; // OUTSIDE WEBSERVERDOCROOT // a d r e s s properties : protected $curpgurl; protected $curpgpath; // 3. V I E W PROPERTIES : protected $form_errors = array(); public function __construct() { // I N I T I A L I Z E (B O O T S T R A P I N G) set_exception_handler(array($this,'logAndDie')); // C R U D (m o d e l) properties : $this->table = 'message'; $this->idname = 'id'; $this->idval_url = isset($_REQUEST[$this->idname]) ? intval($_REQUEST[$this->idname]) : ''; // C R U D actions (cursors, named sql set process comands): $this->cdel_row_byid = // cd_ means cursor for deleting "DELETE FROM $this->table WHERE $this->idname=?"; //$values[]=$parent_id; $this->cr_max_rbrdet_inm; "SELECT MAX(thread_pos) FROM $this->table WHERE thread_id = ? AND parent_id = ?"; $this->cr_byid_nivo_orddet_inm= // cr_ means cursor for reading "SELECT thread_id,level,thread_pos FROM $this->table WHERE $this->idname=?"; $this->cu_incr_rbrdet_inm= "UPDATE $this->table SET thread_pos = thread_pos + 1 WHERE thread_id = ? AND thread_pos >= ?"; $this->cc_mast_or_det="INSERT INTO $this->table " . "($this->idname, thread_id,parent_id, thread_pos, posted_on, level, author, subject, body) " .'VALUES (?,?,?,?,?,?,?,?,?)'; $this->cr_byid= "SELECT author,subject,body,posted_on FROM $this->table WHERE $this->idname = ?"; $this->cr_byid_colname= "SELECT subject FROM $this->table WHERE $this->idname = ?"; // 1. g l o b a l u t l s, s e t t i n g s : $curpgpath =__FILE__; $mastpgpath=$curpgpath; //dirname(__DIR__) ; $this->curpgpath = $curpgpath; require_once($_SERVER['DOCUMENT_ROOT'].'/config_site.php'); $this->curpgurl = $curpgurl ; //htmlentities($_SERVER['PHP_SELF'],ENT_QUOTES); //$_SERVER['DOCUMENT_ROOT'] = eg J:\awww\apl\dev1\ $this->utl = $utl; ?><SCRIPT LANGUAGE="JavaScript"><!-- Begin <?php echo file_get_contents($jsd.$ds.'key_pressed.js');?>//End --></SCRIPT><?php if ('') $utl->phpjsmsg('aaaaaaaaaa key_ pressed.js IS INCLUD E D'); // 2. g l o b a l c r u d : $dbi = 'sqlite3'; $sqlitetbl = 'tema.sqlite3' ; //$dsn='default'; require_once($gloresdir.$ds.'db_conn.php'); //requires klase/dbi.php $this->db = $db; // ****************************************** $title = '<h1>Teme (threads) (poruka-odgovor, zadaća, izbornik) selfjoin PHP PDO SQLite3</h1>' ; $title2 = 'Teme selfjoin'; // ibrowser tab txt $basecss=$cssd.$ds.'style00.css'; //'default' or $cssd.$ds.'style00.css'; include ($gloresdir.'/hdr.php'); //include ($cnfgd.$ds.'hdr.php'); } // e n d _ _ c o n s t r u c t public function ctr() { // M A N A G E (R O U T I N G) // The value of $_REQUEST['cmd'] tells us what to do $cmd = isset($_REQUEST['cmd']) ? $_REQUEST['cmd'] : 'tbl'; // frontend - user actions (backend in sqlitestudio) : switch ($cmd) { case 'delrec': $this->utl->phpjsmsgyn('Obrisati redak ?' , '?cmd=delrecyes&'.$this->idname.'='.$this->idval_url); break; case 'delrecyes': //case 'delrec': if ($this->idval_url) $this->delrec($this->idval_url); else $this->utl->phpjsmsg('Nije zadana šifra retka za brisanje !'); $this->tbl(); break; case 'frm_rpt': // read an self join r o w $this->frm_rpt(); break; case 'frm_post': // display form to post self join r o w $this->frm_post(); break; case 'crerec': // insert - i n s e r t posted self join r o w if ($this->valid()) { // if m s g e is v a l i d, $this->crerec(); // then i n s e r t it $this->tbl(); // and display self join list } else { $this->frm_post(); // otherwise, redisplay the posting form } break; case 'tbl': // show self join r o w s list default: $this->tbl(); break; } } // F R O N T E N D - user actions : protected function crerec() { // crud1. FRONTEND A D D SELFJOIN ROW $parent_id = isset($_REQUEST['parent_id']) ? intval($_REQUEST['parent_id']) : 0; // Make sure m s g e doesn't change while we're working with it. $this->db->beginTransaction(); $this->inTransaction = true; // is this m s g e a reply? if ($parent_id) { // get the thread, level, and thread_pos of the parent m s g e //"SELECT thread_id,level,thread_pos FROM $this->table WHERE $this->idname=?"; $st = $this->db->prepare($this->cr_byid_nivo_orddet_inm); $st->execute(array($parent_id)); $parent = $st->fetch(); // a reply's level is one greater than its parent's $level = $parent['level'] + 1; // what's biggest thread_pos in this thread among m s g e s with the same parent? //"SELECT MAX(thread_pos) FROM $this->table WHERE thread_id = ? AND parent_id = ?"; $st = $this->db->prepare($this->cr_max_rbrdet_inm); $st->execute(array($parent['thread_id'], $parent_id)); $thread_pos = $st->fetchColumn(0); // are there existing replies to this parent? if ($thread_pos) { // this thread_pos goes after the biggest existing one $thread_pos++; } else { // this is the first reply, so put it right after the parent $thread_pos = $parent['thread_pos'] + 1; } // increment thread_pos of all m s g e s in the thread that come after this one //"UPDATE $this->table SET thread_pos = thread_pos + 1 WHERE thread_id = ? AND thread_pos >= ?"; $st = $this->db->prepare($this->cu_incr_rbrdet_inm); $st->execute(array($parent['thread_id'], $thread_pos)); // the new m s g e should be i n s. with the parent's thread_id $thread_id = $parent['thread_id']; } else { // m s g e is not reply, so it's the s t a r t of new t h r e a d $thread_id = $this->db->query( "SELECT MAX(thread_id) + 1 FROM $this->table")->fetchColumn(0); // If there are no rows yet, make sure we s t a r t at 1 for thread_id if (! $thread_id) { $thread_id = 1; } $level = 0; $thread_pos = 0; } // i n s e r t m s g e into DB. Using prepare() and execute() makes sure that all fields are properly quoted //"INSERT INTO $this->table " //. "($this->idname, thread_id,parent_id, thread_pos, posted_on, level, author, subject, body) " //.'VALUES (?,?,?,?,?,?,?,?,?)'; $st = $this->db->prepare($this->cc_mast_or_det); $st->execute(array( null, $thread_id, $parent_id, $thread_pos, date('c'), $level ,$_REQUEST['author'], $_REQUEST['subject'],$_REQUEST['body'])); // Commit all the operations $this->db->commit(); $this->inTransaction = false; } protected function tbl() // crud2. D I S P L A Y H I E R A R C H Y { //print '<h2><a href="http://dev:8083/test/books/a01cookbook/tema.php">Teme (poruka-odgovor)</a></h2>'; // provide a way to p o s t non-reply m s g e // IZBORNIK t b l - i c e : // t b l h d r r o w (action menu) : $lnk_addmaster = '<strong>'."<a class='btn' href='".$this->curpgurl ."?cmd=frm_post'>Otvoriti novu temu (nit, thread)</a>" .'</strong> '; print $lnk_addmaster; if ('1') print ' Help = Klik Rbr. |...| = | bytes|idniti, rbruniti, nivo, id, idviši| ' .'<hr/>'; //order m s g s by their thread (thread_id) and their position within thread (thread_pos) $st = $this->db->query( "SELECT $this->idname, subject,author" .",LENGTH(body) body_length,posted_on,level,thread_id,thread_pos ,parent_id,url" ." FROM $this->table ORDER BY thread_id,thread_pos"); while ($row = $st->fetch()) { $when = date('Y-m-d H:i', strtotime($row['posted_on'])); // indent m s g e s with level > 0 print '<font style="font-family: Courier;">' .str_repeat(' ',2 * $row['level']) .'</font>'; // print info about m s g with // - link to open page eg http://dev1:8083/01apl/04tema/tema.php // - link to read it print // ----------- 1. thread_id (NIT) = ORD.NO : ( ($row['level'] == 0) // branch root ? //'<strong>' // Help (msg content report) = Klik Rbr : "<a href='" . $this->curpgurl . "?cmd=frm_rpt&$this->idname={$row[$this->idname]}" ."'>" . '<span class="btnsmall">' .'<font style="color: black; font-family: Courier;">' // background-color: red; .str_repeat(' ', (6 - strlen($row['thread_id'])) ) .$row['thread_id'] //.'</strong>' .'</font>' . '</span>' .'</a>' .'. ' : // ----------- 2. thread_pos = ORD.NO IN THREAD - Help (msg content report) = Klik Rbr u niti : "<a href='" . $this->curpgurl . "?cmd=frm_rpt&$this->idname={$row[$this->idname]}" ."'>" //.' '.$row['thread_pos'].'. ' .'</strong>' . '<span>' .'<font style="color: black; background-color: white; font-family: Courier;">' .str_repeat(' ', (6 - strlen($row['thread_pos'])) ) .$row['thread_pos'] //.'</strong>' .'</font>' . '</span>' .'</a>' .'. ' ) // Klik msg to open page : // eg http://dev1:8083/index.php?cmd=lsweb&dir=J:\awww\apl\dev1\test\01info ."<a href='" . htmlentities($row['url'],ENT_QUOTES) ."'>" . htmlentities($row['subject'],ENT_QUOTES) .'</strong>' . '</a> ' . ' by '. htmlentities($row['author'],ENT_QUOTES) . ' @ ' . htmlentities($when,ENT_QUOTES) ; // L I N K c m d = d e l r e c printf('<a href="%s?cmd=delrec&'.$this->idname.'=%s' //.'&'.'rrgo'.'=%s' .'">'.'%s</a>' , $this->curpgurl , $row[$this->idname] //, $pgrr1 , '<font style="color: red;">'.' |'.'</font>' .'BRIŠI' // d e l e t e .'<font style="color: red;">'.'|'.'</font>' ); print " {$row['body_length']} " // bytes| . " |PROMJ " // u p d a t e ; if ('1') print "|{$row['thread_id']},{$row['thread_pos']},{$row['level']} ,{$row[$this->idname]},{$row['parent_id']}|"; print '<br/>'; } // e n d p r i n t r o w s print '<hr/>'.$lnk_addmaster; } // e n d f n t b l ( ) protected function frm_rpt() { // crud3. R O W REPORT (m s g b o d y...) // make sure the m s g e i d we are passed is an integer and really represents a m s g e if (! isset($_REQUEST[$this->idname])) { throw new Exception('Nije formirana šifra poruke'); } $id = intval($_REQUEST[$this->idname]); //"SELECT author,subject,body,posted_on FROM $this->table WHERE $this->idname = ?"; $st = $this->db->prepare($this->cr_byid); $st->execute(array($id)); $msg = $st->fetch(); if (! $msg) { throw new Exception('Loša šifra poruke'); } /* don't display user-entered HTML, but display newlines as HTML line breaks */ $body = str_replace('{{/strong}}','</strong>', str_replace('{{strong}}','<strong>', nl2br(htmlentities($msg['body'])) )); // while (false !== strpos($body, '{{url}}')) { $beg = strpos($body, '{{url}}'); $end = strpos($body, '{{/url}}'); $url = substr($body, $beg+7, $end - $beg -7); $url2 = '<a href="'.$url.'">'.$url.'</a>'; // </strong> $body = str_replace('{{url}}'.$url.'{{/url}}',$url2,$body); //$body .= '<br />'.$url2; } // // display m s g e with links to reply and return to the m s g e list $subject = htmlentities($msg['subject']); $author = htmlentities($msg['author']); // --------------------------------------- // h d r m e n u f r m _ v i e w - a // -------------------------------------- print<<<_HTML_ <h2>$subject</h2> <h3> by $author <a href="$this->curpgurl?cmd=frm_post&parent_id=$id">Odgovor</a> <a href="$this->curpgurl?cmd=tbl">Stablo poruka</a> </h3> <hr/> <p>$body</p> _HTML_; } // tbl() protected function frm_post() // crud4. ENTER USER DATA & PROCESS IT { $safe = array(); foreach (array('author','subject','body') as $field) { // escape input values : if (isset($_POST[$field])) { $safe[$field] = htmlentities($_POST[$field]); } else { $safe[$field] = ''; } // make err m s g s display in red : if (isset($this->form_errors[$field])) { $this->form_errors[$field] = '<span style="color: red">' . $this->form_errors[$field] . '</span><br/>'; } else { $this->form_errors[$field] = ''; } } // e n d f o r e a c h // is this m s g e reply ? if (isset($_REQUEST['parent_id']) && $parent_id = intval($_REQUEST['parent_id'])) { // send parent_id along when form is submitted $parent_field = sprintf( '<input type="hidden" name="parent_id" value="$this->idname" />' , $parent_id); // if no subject's been passed in, use parent's subject if (! strlen($safe['subject'])) { //"SELECT subject FROM $this->table WHERE $this->idname = ?"; $st = $this->db->prepare($this->cr_byid_colname); $st->execute(array($parent_id)); $parent_subject = $st->fetchColumn(0); /* prefix 'Re: ' to parent subject if it exists and doesn't already have 'Re:' */ $safe['subject'] = htmlentities($parent_subject); if ( $parent_subject && (! preg_match('/^re:/i',$parent_subject))) { $safe['subject'] = "Re: {$safe['subject']}"; } } } else { $parent_field = ''; } // display posting form, with errors and default values print<<<_HTML_ <form method="post" action="$this->curpgurl"> <table> <tr> <td>Your Name:</td> <td>{$this->form_errors['author']} <input type="text" name="author" value="{$safe['author']}" /> </td> <tr> <td>Subject:</td> <td>{$this->form_errors['subject']} <input type="text" name="subject" value="{$safe['subject']}" /> </td> <tr> <td>Poruka:</td> <td>{$this->form_errors['body']} <textarea rows="4" cols="30" wrap="physical" name="body">{$safe['body']}</textarea> </td> <tr><td colspan="2"><input type="submit" value="Pošaljite poruku" /></td></tr> </table> $parent_field <input type="hidden" name="cmd" value="crerec" /> </form> _HTML_; } // B A C K E N D - administrator actions : protected function delrec($parent_id) { // crud5 BACKEND D ELETE R OW //$parent_id = isset($_REQUEST[$this->idname]) ? intval($_REQUEST[$this->idname]) : 0; // $this->idval_url //basename(__FILE__).' SAYS'.'<br>'.'$id'.'=='.$parent_id.'==' if ('') $this->utl->phpjsmsg('***** '.__FUNCTION__.'() SAYS: ' .'<br>'.'$dml=***'.$this->cdel_row_byid.'***<br>'.'?=$parent_id=***' .$parent_id.'***'); // is this m s g e a reply? if ($parent_id) { // Make sure m s g e doesn't change while we're working with it. $this->db->beginTransaction(); $this->inTransaction = true; //"DELETE FROM $this->table WHERE $this->idname=?"; //$values[]=$parent_id; $st = $this->db->prepare($this->cdel_row_byid); //or $this->db->get_con()->prepare ? $st->execute(array($parent_id)); // fetchAll() is needed only for s e l e c t //Commit all the operations $this->db->commit(); $this->inTransaction = false; } } // 5. makes sure something is entered in each field : protected function valid() { $this->form_errors = array(); // R E Q U I R E D U S E R D A T A : if (! (isset($_POST['author']) && strlen(trim($_POST['author'])))) { $this->form_errors['author'] = 'Upišite vaše ime (autor).'; } if (! (isset($_POST['subject']) && strlen(trim($_POST['subject'])))) { $this->form_errors['subject'] = 'Upišite naslov poruke.'; } if (! (isset($_POST['body']) && strlen(trim($_POST['body'])))) { $this->form_errors['body'] = 'Upišite tekst poruke.'; } return (count($this->form_errors) == 0); } // 6. protected function logAndDie(Exception $e) { print 'ERROR: ' . htmlentities($e->getMessage()); if ($this->db && $this->db->inTransaction()) { $this->db->rollback(); } exit(); } } // e n d c l a s s // <strong><a href="http://...">http://...</a></strong> // if (false !== strpos($string, $substring)) { /* found it! */ } // if (strpos($haystack, $needle) !== false) echo 'match!'; // $withoutCommas = is_numeric(str_replace(',', '', $number)); /* ALTER TABLE message RENAME TO sqlitestudio_temp_table; CREATE TABLE message ( id INTEGER PRIMARY KEY AUTOINCREMENT, subject CHAR (255), url VARCHAR (500), thread_id [INT UNSIGNED] NOT NULL, thread_pos [INT UNSIGNED] NOT NULL, level [INT UNSIGNED] NOT NULL, parent_id [INT UNSIGNED] NOT NULL, author CHAR (255), body MEDIUMTEXT, posted_on DATETIME NOT NULL ); INSERT INTO message ( id ,subject ,url ,thread_id ,thread_pos ,level ,parent_id ,author ,body ,posted_on ) SELECT id ,subject ,url ,thread_id ,thread_pos ,level ,parent_id ,author ,body ,posted_on FROM sqlitestudio_temp_table; DROP TABLE sqlitestudio_temp_table; TO INCLUDE RESULTS OF EXECUTING FN OR EXPRESSION WITHIN A STRING You can put vars, obj.prop, array el. (if subscript is unquoted) directly in double-quoted strings: eg print "You owe $amounts[payment] immediately."; eg print "My circle's diameter is $circle->diameter inches."; Curly braces around more complicated expressions to interpolate them into a string: print "I have {$children} children."; print "You owe {$amounts['payment']} immediately."; print "My circle's diameter is {$circle->getDiameter()} inches."; Direct interpolation or string concat. also works with heredocs: print <<< END Right now, the time is END . strftime('%c') . <<< END but tomorrow it will be END . strftime('%c',time() + 86400); {{url}} http://dev1:8083/my_dev/pdo/tema/tema.php {{/url}} J:\awww\apl\dev1\my_dev\pdo\tema\tema.php J:\dev_web\htdocs\test\books\a01cookbook\tema.php Promjene podataka programom J:\aplp\aplp\sqlitestudio\SQLiteStudio.exe SELFJOIN TABLE : 1. C INSERT frm data, 2. R DISPLAY tbl, row, 3. V VALIDATE, E set_exception_handler Not neccessarily here: U UPDATE and D DELETE WITH J:\aplp\aplp\sqlitestudio\SQLiteStudio.exe */