Jump to content
php.lv forumi

Kā uzrakstīt vienkāršu MVC Framework-u


codez

Recommended Posts

Tā kā man kāds cilvēks palūdza pastāstīt par MVC, tad nolēmu šajā procesā radušos materiālu atreferēt arī šeit. Iespējams daudziem varētu noderēt lābākas programmēšanas prakses ieviešanā.

 

MVC ir saīsinājums no Model-View-Controller

Ideja aptuveni tāda, ka request-a gadījumā tiek izsaukts kontroleris, kurš apstrādā requestu, ievāc datus no modeļa un nodod tos view-am, kas vienkāršā gadījumā ir parasts templeits.

Smalkāk lasīt wikipēdijā.

 

Tālāk nodemonstrēšu kā uztaisīt primitīvu MVC Framework-u (FW).

 

No sākuma ir jāizveido principi pēc kādiem būs būvēta aplikācijas saišu struktūra.

Šajā piemēra tiks izmantota salīdzinoši elementāra sistēma:

domains.lv/kontroleris/parametrs1/parametrs2/parametrs3/utt

 

Šādā struktūrā katrs kontroleris būtu kāda aplikācijas lapa, piemārm:

 

/user/123

kontroleris būtu user ar parametru 123,

 

/article/100-Hello-world/3

kotroleris būtu article, kur pirmais parametrs būtu raksta id+nosaukums, bet otrais komentāra lapaspuse

 

Otrs populārs veids ir:

domains.lv/kontroleris/darbība/parametrs1/parametrs2/utt...

Tad katram kontrolerim ir vairākas iespējamās darbības (update_kautko, save_kautko, post_kautko)

Bet tākā esmu ajax-īgu aplikāciju cienītājs, tad parasti katrā no lapām šīs darbības tiek veiktas ajax-īgi un pietiek, ka katram kontrolerim ir tikai viena pamatdarbība.

Ja nu gadījumā ir vajadzīga šāda neajaxīga papilddarbība, tad tai var izveidotu atsevišķu kontroleri, kurš veic šo darbību.

 

Turpinot FW veidošanu, sāksim ar to, ka mēs vēlamies, lai visi pieprasījumi mums ietu caur vienu failu, tāpēc izmantojam .htaccess

 

Options +FollowSymlinks 

RewriteEngine On 
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !^(.+)\.(css|js|jpg|gif|png|ico)$
RewriteRule ^(.*) index.php?p=$1 [QSA,L]

php_flag magic_quotes_gpc Off
php_flag register_globals Off

 

šis .htaccess fails lie apache serverim, piemēram, šādu requestu:

domeins.lv/article/100-hello-world/3

pārveidot par

domeins.lv/index.php?p=article/100-hello-world/3

 

Vienīgie izņēmumi ir faili, kuri beidzas ar .css, .js, utt...

Šos failus mēs servējam pa tiešo, šeit mums index.php izsaukt nav nepieciešams.

 

Tā kā visi pieprasījumi ies caur index.php failu, tad ar to arī sāksim.

 

Šajā failā izveidosim Core klasi, kuras galvenais uzdevums būs apstrādāt pieprasījumu un palaist nepieciešamo kontrolleri.

 

class Core{   	
 function run(){  
   $this->path=explode('/',(string)@$_GET['p']);
   if ($this->path[0]==''){$this->path[0] = 'index';}	
   echo $this->runController($this->path[0]);
 }
}

Tā kā mums pieprasījums tiek pārveidots uz $_GET['p'] parametru, tad to sadalam daļās.

Ja gadījumā tiek pieprasīta pamatlapa domeins.lv/, tad jau šeit nosakam, ka tiks izmantots index kontroleris.

Pēdējā rindā izsaucam kontrolera palaišanas metodi - runController()

 

Tālāk mums ir jāizveido runController(), kurai parametrs būs palaižamā kontrolera nosaukums.

 

 function runController($n){
   if (file_exists($n.'.ctrl')) {
     $m='C'.$n;
     $ctrl=new $m();
     return $ctrl->run();
   } else {
     return ($n!='notfound')?$this->runController('notfound'):'';
   }    		
 } 

 

Tātad visi palaižamie kontroleri glabāsies .ctrl failos un viņu atbilstošie viewi būs templeiti un glabāsies .tpl failos

Šeit mēs no sākuma pārbaudām vai kotrolera fails eksistē, ja neeksistē, tad palaižam notfound kontroleri, kurš atbildēs par neatrasto lapu apstrādi. Šeit mēs varam vai nu vienkārši redirektot uz sākumlapu vai attēlot lapu ar 404 paziņojumu.

Ja kontroleris eksistē, tad palaižam izsaukto kontroleri.

 

Kā redzam, tad, piemēram, kotroleris user tiek pieprasīts kā Cuser klase.

 

     $m='C'.$n;
     $ctrl=new $m();

 

Šī MVC FW pamatā, lai būtu iespējams veidot kontrolerus, modeļus, bibliotēkas ar vienādiem nosaukumiem, klasēm tiek piešķirti prefiksi, attiecīgi:

C - kontrolerim

M - modelim

L - bibliotēkai

H - neredzamam kontrolerim, kuru var izmantot vai extendod citi kontroleri, bet nevar izsaukt

 

attiecīgi, lai vieglāk operēt ar failiem, tad katrai grupai ir savs extension-s.

kontrolierim .ctrl

modelim .mdl

bibliotēkai .lib

neredzamam kontrolerim .hctrl

 

bastoties uz šiem principiem tiek izveidota autoload funkcija, kura nodrošinās automātisku to failu ielādi, kuru klases php neeksistēs.

Tas pilnībā atbrīvos pēc inlude vajadzības, jo tās klases, kuras tiks izmantotas, automātiski tiks ielādētas.

 

Attiecīgi izveidojam Core klasē divas metodes:

- autoload, kura nodrošinās klašu ielādi

- classFile, kura atgriezīs klases faila nosaukumu atkarībā no pašas klases nosaukuma.

 

 function classFile($c){
   $ext=array('C'=>'ctrl','H'=>'hctrl','M'=>'mdl','L'=>'lib');  
   return strtolower(substr($c,1)).'.'.$ext[$c[0]];    
 }
 public static function autoload($c){
   require_once Core::classFile($c);
 }

 

un Core::run metodei pievieojam autoload funkcijas reģistrēšanu:

 

   spl_autoload_register('Core::autoload');  

 

Ērtības labad Core klasi izveidojam kā Singletonu, lai tās intancei varētu piekļūt no jebkuras vietas:

 

 private static $i; 
 public static function i()	{
   return (!self::$i instanceof self)?(self::$i = new self):self::$i;
 }

 

Un pēc Core klases definīcijas palaižam to:

 

Core::i()->run();

 

Tālāk mums ir nepieciešam paša Controller-a klase, ko veidojam tajā pašā failā, jo viņa būs vajadzīga visiem requestiem, kuri ies caur index.php

 

Kopā iegūstam šādu index.php failu:

 

index.php

 

Principā šo jau var nosaukt par mikro MVC FW, jo tas pieļauj kontroleru un templeitu veidošanu un atomātisku palaišanu.

Kontrolerus veidojam kā klases ar prefixu C nosaukum, ekstendojot controller klasei vai kāda cita kontrolera klasi un ar funkciju index, kura tiks izsaukta palaižot kontrolleri.

 

Piemēram izveidojam index.ctrl:

<?php
class CIndex extends Controller{
 function index(){
   $this['a']='Hello from index.ctrl';
 }
}

 

un index.tpl

Hello from index.tpl <br />
and <br />
<?php echo $a; ?>

 

un palaižam: domeins.lv/

 

Nākošais, ko mēs vēlamies panākt, lai katrai lapas sadaļai varētu pievienot kādu kopēju rāmi: headeri, footeri, menu.

Lai šādu ideju realizēt, kontrolerim ir izveidots $parent parametrs, kura izveidošanas gadījumā, tiek izsaukts šis parent kontroleris, kuram par child_content parametrā tiek ievietots pirmā viņu izsaukušā kontrolera ģenerētais saturs.

Protams šo parent kontroleri mēs nevēlamies atļaut izsaukt pa tiešo, tāpēc tas būs hidden kontroleris un atradīsies failā .hctrl ar klases prefixu H.

 

Tātad sauksim šo kontroleri par main un izveidosim main.hctrl:

 

<?php
class HMain extends Controller{
 function index(){
   $this->addCss('/main.css');    
   if (file_exists(Core::i()->path[0].'.css')) {$this->addCss('/'.Core::i()->path[0].'.css');}
   if (file_exists(Core::i()->path[0].'.js')) {$this->addCss('/'.Core::i()->path[0].'.js');}
 }  
 function addCSS($s){
   if (!isset($this->_css)) {$this->_css=array();}
   $this->_css[]=$s;
 }
 function addJS($s){
   if (!isset($this->_js)) {$this->_js=array();}
   $this->_js[]=$s;
 } 
 function setTitle($s){
   $this->_title=$s;
 }  
 function renderCSS(){
   foreach ((array)@$this->_css as $s){
     echo '  <link rel="stylesheet" type="text/css" href="'.$s.'" />'."\n";
   }
 }
 function renderJS(){
   foreach ((array)@$this->_js as $s){
     echo '<script type="text/javascript" src="'.$s.'" /></script>'."\n";
   }
 }
 function renderTitle(){
   echo '<title>'.(string)@$this->_title.'</title>'."\n";
 }    
} 

 

un tam atbilstošu templeitu main.tpl:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
 <?php 
   $this->renderTitle();
 ?>

 <link rel="shortcut icon" href="/favicon.ico" /> 
 <?php
   $this->renderCSS();
   $this->renderJS();    
 ?>
</head>
<body>
 <div id="wrapper">
   <div id="header">
     <div id="title">Header</div>
     <div id="menu">
       <a href="/">Home</a>| 
       <a href="/search">Search</a>| 
       <a href="/help">Help</a>
     </div>
   </div>
   <div id="content">
     <?php echo $child_content; ?>
   </div>
   <div id="footer">
     Footer
   </div>
 </div>
</body>
</html>

 

$child_content mainīgais attiecīgi satur saturu no kontrolera, kurš šo izsauca kā savu parent kontroleti, tāpēc to attiecīgi ievietojam tajā lapas vietā, kur vēlamies.

Kā redzams HMain::index() metodē tiek pievienots CSS fails /main.css, tad attiecīgi izveidojam arī šo failu:

 

body{
 margin:0px;
 padding:0px;  
}
#wrapper{
 width:970px;
 margin:0 auto;
}
#header{
 height:64px;
 background:#dfd;
}
#title{
 font-Size:28px;
}
#footer{
 height:32px;
 font-Size:28px;
 background:#dfd;
}

 

Kā redzams vēl metodē HMain::index šīs divas rindas:

   if (file_exists(Core::i()->path[0].'.css')) {$this->addCss('/'.Core::i()->path[0].'.css');}
   if (file_exists(Core::i()->path[0].'.js')) {$this->addCss('/'.Core::i()->path[0].'.js');}

nodrošina to, ka attiecīgajam izsauktajam kontrolerim pēc tāda paša nosaukuma tiek pievienoti automātiski css un js faili, ja tādi eksitē.

Piemēram, ja izsauc domeins.lv/user/100, tad pievieno user.css un user.js, ja šie faili eksistē.

Šāda prakse, manuprāt, ir ļoti ērta.

 

Kad mums ir izveidots galvenais kontroleris, varam savam index kontrolerim pievienot $parent parametru - index.ctrl:

 

<?php
class CIndex extends Controller{
 public $parent='HMain';
 function index(){
   $this['a']='Hello from index.ctrl';
 }
}

 

Varam tagad palaist domeins.lv/ un redzēsim ka tagat jau index.ctrl kontrolera saturs ir ievietots main.hctrl kontrolera saturā.

 

Tālāk jau katrai jaunai sadaļai veidojam .ctrl un .tpl failus pēc tāda paša patterna, kā index.ctrl

Piemēram, varam izveidot notfound kontroleri, kurš tiks izsaukts gadījumos, ja netiek atrasts pieprasītais kontroleris - notfound.ctrl:

 

<?php
class CNotfound extends Controller{
 public $parent='HMain';
 function index(){
   Header('location:/');
   exit;
 }
}

 

Šijā piemērā neeksistējošu kontroleru pieprasīšanas gadījumā, lapa tiek redirektota uz /.

Varam arī atstāt index metodi tukšu un izveidot notfound.tpl un notfound.css failus ar kuru palīdzību izveidojam smuku 404 kļūdas paziņojumu.

 

Šajā stadijā vajadzētu būt failiem:

.htaccess

index.php

main.hctrl

main.tpl

main.css

index.ctrl

index.tpl

notfound.ctrl

 

Kopumā šis ir ļoti vienkārš piemērs kā veidot MVC FW.

Būtībā labāk būtu, ja visi php koda faili atrastos ārpus publiski pieejamās mapes un tāpat arī visi neatrastos vienā mapē, bet būtu sakartoti, piemēram, pa controllers, libs, models mapēm.

 

Ja būs laiks un gribēšna, tad varētu šim rakstam izveidot turpinājumu, kur uz šī piemēra tālāk nodemonstrētu:

- kā pievienot db bibliotēku, kuras izmantošana automātiski būtu droša pret SQL injekcijām,

- kā forši strādāt ar ajax request-iem,

- nedaudz par autorizāciju un token-u izmantošanu pret XSRF.

Edited by codez
Link to comment
Share on other sites

  • Replies 58
  • Created
  • Last Reply

Top Posters In This Topic

Ouch.. Šo visu izlasot sapratu, ka nesaprotu neko par PHP... Nu ko, lūk tavs rakstiņš sēdēs manos bookmarkos un varēšu viņu ik pāris dienas vērt vaļā un lasīt līdz man tas aizies. Tad arī sākšu mācīties tālāk par MVC.

 

Raksts ļoti labs, liels paldies! :)

Link to comment
Share on other sites

Varbūt līdz galam neiedziļinājos pasākumā, bet mani kaut kā mazliet nepārliecina šis pārlieku vienkāršotais mehānisms. Kādā veidā, tu, piemēram, nodrošini to, ka parent controller (tavā gadījumā HMain) smuki sazīmē, iekš menu, kurā lapas sadaļā (kontrolierī) tu šobrīd atrodies? Neba nu tu taisi hidden kontrolieri/skatu/templeitu katrai sadaļai.

 

Vai šāds risinājums tevi gadījumā dikti neierobežo uz to, ka tev reālās lapas skats var sastāvēt tikai no viena kontroliera saģenerētā satura? Es teiksim iedomājos triviālu lapu, kurā es redzu galveno saturu centrālajā kolonā un, piemēram, sāna kolonā jaunākos foruma ierakstus, kas tiek ņemti no pavisam cita kontroliera/skata/modeļa. Pēc šī vienkāršotā modeļa sanāk, ka

a) centrālās kolonas kontrolierim ir jāzina, kas viņam ir jāsaģenerē sāna kolonā un attiecīgi arī sāna kolona ir jāzīmē attiecīgā kontroliera skatam

b) HMain ir jāspēj izdomāt, kuri citi kontrolieri ir jāizpilda, lai saģenerētu sāna kolonu.

 

 

Tb. tas, ko es gribu pateikt - reālā dzīvē parasti tiek lietots nevis pliks MVC, kuru tu te esi nodemonstrējis, bet kaut kas vairāk uz HMVC pusi ( http://techportal.ibuildings.com/2010/02/22/scaling-web-applications-with-hmvc/ )

 

 

+ vēl par pašu konceptu:

nodrošina to, ka attiecīgajam izsauktajam kontrolerim pēc tāda paša nosaukuma tiek pievienoti automātiski css un js faili, ja tādi eksitē.

Piemēram, ja izsauc domeins.lv/user/100, tad pievieno user.css un user.js, ja šie faili eksistē.

Šāda prakse, manuprāt, ir ļoti ērta.

 

Manuprāt šāda veida pārbaudas uz failu eksistenci ir nepareizs piegājiens, tam tu vari uzrakstīt kaut vai kontroliera init() f-ju, kurā vienu reizi nodefinēt tos failus, ja tie ir nepieciešami. Ja lapas ar minimālu apmeklējumu nekādu starpību nejutīs, tad lielas slodzes lapām bariņš ar file_exists pārbaudēm jau var radīt mērenu jautrību. Pie tam vēl iedomājoties, kas notiek tad, ja tu teiksim uzbūvē tomēr HMVC un vienas lapas ielādes laikā tev tiek izpildīti teiksim 10 kontrolieri, tad paliek pavisam jautri.

Link to comment
Share on other sites

Varbūt līdz galam neiedziļinājos pasākumā, bet mani kaut kā mazliet nepārliecina šis pārlieku vienkāršotais mehānisms. Kādā veidā, tu, piemēram, nodrošini to, ka parent controller (tavā gadījumā HMain) smuki sazīmē, iekš menu, kurā lapas sadaļā (kontrolierī) tu šobrīd atrodies? Neba nu tu taisi hidden kontrolieri/skatu/templeitu katrai sadaļai.

 

Lai gan šis freimworka piemērs ir stipri vienkāršots, taču mēģināšu parādīt, kā var aptuveni risināt tavas apskatītās problēmas.

Ja HMain kontrolerim ir svarīgi iezīmēt menu, lapu kurā atrodies, tad to child kontrolieris var nodot vienkārši kā parametru, piemēram:

$this->_IParent['menuid']=5;

vai arī varam papildināt parent interface ar speciālu funckiju un tad padot šādi:

$this->_IParent->setMenu(5);

varam arī kontrolera klasei izveidot skaistu metodi parent instance piekļuvei:

$this->parent()->setMenu(5);

 

 

Vai šāds risinājums tevi gadījumā dikti neierobežo uz to, ka tev reālās lapas skats var sastāvēt tikai no viena kontroliera saģenerētā satura? Es teiksim iedomājos triviālu lapu, kurā es redzu galveno saturu centrālajā kolonā un, piemēram, sāna kolonā jaunākos foruma ierakstus, kas tiek ņemti no pavisam cita kontroliera/skata/modeļa. Pēc šī vienkāršotā modeļa sanāk, ka

a) centrālās kolonas kontrolierim ir jāzina, kas viņam ir jāsaģenerē sāna kolonā un attiecīgi arī sāna kolona ir jāzīmē attiecīgā kontroliera skatam

b) HMain ir jāspēj izdomāt, kuri citi kontrolieri ir jāizpilda, lai saģenerētu sāna kolonu.

 

Jebkurā gadījumā requesta parametri visdrīzāk būs paredzēti vienam no kontroleriem (kolonnām) un pārējie būs neatkarīgi no requesta parametriem, piemēram, reklāma, jaunākās ziņas, pēdējo posti, utt.

Bet to var panākt pavisam vienkārši.

untaisam main2.hctrl, kuram parent ir main.hctrl

main.ctrl atbildēs par headeri, menu, footeri, kamēr main2.ctrl būs ar 3 kolonnām, tālāk būs piemēram articles.ctrl, kurš ekstendo šo main2.hctrl un atrodas vienā no kollonām.

bet parējās divās mēs varam ielādēt saturu no citiem kontrolieriem pa taisno templeitā main2.tpl šādā veidā:

<div id="kolonna2">
 <?php
   $ctrl = new HRecentPosts();
   echo $ctrl->run();
 ?>
</div>

Ja mums šāda kontroleru lādēšana tiešām ir bieži vajadzīga, varam Core kādā metodē ielikt šīs divas rindiņas, lai varam rakstīt šādi:

<div id="kolonna2">
 <?php
   echo Core::renderController('HRecentPosts');
 ?>
</div>

 

Varam arī pašā kontrolerī ladīt cita kontrolera saģenerēto:

// hmain.ctrl
class HMain2 extends Controller{
 $parent='HMain';
 function index(){
   $this['recentposts']=Core::renderController('HRecentPosts');
   $this['add1']=Core::rendercontroller('HAdd1');
 }
}

// main.tpl
<div id="col1">
 <?php echo $child_content; ?>
</div>
<div id="col2">
 <?php echo $recentposts; ?>
</div>
<div id="col3">
 <?php echo $add1; ?>
</div>

//article.ctrl
class CArticle extends Controller{
 $parent='HMain2';
 function index(){
   $this['article']=MArticle::load((int)@Core::i()->path[1]);
 }
}

//article.tpl
<?php echo $article['title']; ?><br />
<?php echo $article['text']; ?>

Protams pēdējā piemērā ir jāizveido MArticle modelis.

Šī frameworka gadījumā varam izmantot jau gatavos opensource ORM FW vai uztaisīt paši savu Model vai ActiveRecord klasi.

 

 

 

Tb. tas, ko es gribu pateikt - reālā dzīvē parasti tiek lietots nevis pliks MVC, kuru tu te esi nodemonstrējis, bet kaut kas vairāk uz HMVC pusi ( http://techportal.ibuildings.com/2010/02/22/scaling-web-applications-with-hmvc/ )

HMVC ir daudzi performances mīnusi attiecībā uz projektiem, kurus var palaist uz salīdzinoši nelielas serveru arhitektūras, nemaz nerunājot par projektiem, kuriem pietiek ar vienu kasti. Šie performacnes mīnusi rodās no pārlieku lielās vēlmes sadalīt aplikāciju pilnībā atdalāmos moduļos.

Bet, ja gribi, varu uztaisīt arī šādu nelielu demo HMVC piemēram - 90 rindiņu vietā varētu būt, ka aizņems kādas 150 rindiņas koda.

Un tomēr praksē MVC vēl skaita ziņā krietni dominē pār HMVC.

Šī demonstrācīja vairāk ir kā ceļa norāde tiem, kuri web aplikācijas izstrādē vēl pēc šādas metodes:

include('db.php')
include('header.php');
//šeit kaut ko izdarām vai arī ieliekam case ar daudzām inclūdēm
include('foorer.php')

 

 

Manuprāt šāda veida pārbaudas uz failu eksistenci ir nepareizs piegājiens, tam tu vari uzrakstīt kaut vai kontroliera init() f-ju, kurā vienu reizi nodefinēt tos failus, ja tie ir nepieciešami. Ja lapas ar minimālu apmeklējumu nekādu starpību nejutīs, tad lielas slodzes lapām bariņš ar file_exists pārbaudēm jau var radīt mērenu jautrību. Pie tam vēl iedomājoties, kas notiek tad, ja tu teiksim uzbūvē tomēr HMVC un vienas lapas ielādes laikā tev tiek izpildīti teiksim 10 kontrolieri, tad paliek pavisam jautri.

Varam tās automātiskās pārbaudes protams ņemt ārā un katram kontrolerim, kuram ir vajadzīgi css un js faili, vienkārši index() metodē rakstīt:

 $this->_ITop->addCSS('articles.css');

_ITop šoreiz ir instance pašam pēdējam kontrolerim hirarhijā

Smukuma pēc varam arī Controller klasē izveidot metodi top, lai smukāk izskatās:

 $this->top()->addCSS('articles.css');

 

Bet par to file_exists slodzi gan nepiekrītu. file_exists tiek izsaukts proporcionāli nolasāmo kontrolieru skaitam. Respektīvi vienam kontrolierim 2 file_exists. Manuprāt tas nav performances bremzētājs, jo uz katru file_exists pretī ir kontrolera nolasīšana pārsēšana un palaišana, parent kontroleira nolasīšana, pārsēšana, palaišana, abuk šo kontrolieru templeitu nolasīšanas un pārsēšana.

Neesmu testēji, bet pieļauju, ka tas file_exists reālā projektā sastādīs mazāk par 0.1% pieprasījuma ģenerētās slodzes.

 

 

 

 

Kā redzams, tad pat šāds 90 rindiņu MVC FW, spēj elementāri risināt lielu skaitu problēmu, ar kurām mēs sastopamies web izstrādē, tai pašā laikā nodrošinot gandrīz maksimālu performanci.

Protams es neapgalvoju, ka šis ir MVC, kurš ir gatavs reālās dzīves projektu izstrādei, bet drīzāk norāde virzienam kādā vajadzētu padomāt lielai daļai programmētāju, kuri vēl ir savu pamatiemaņu apgūšanas stadijā.

Edited by codez
Link to comment
Share on other sites

P.S.

Patiesībā tikko sapratu, ka mans nodemonstrētais mikro MVC FW spēj realizēt arī HMVC paternu, ko es nodemonstrēju iepriekšējā komentāra piemēros, vienīgā atšķirība no lielajiem HMVC freimworkiem ir tā, ka viņš to dara viena servera ietvaros, nevis visā web slānī.

 

 

EDIT:

Par HMVC paradīto skeilošanas piemēru (http://techportal.ib...ions-with-hmvc/):

Manuprāt, tā vietā, lai dažādās aplikācijas daļas izvietotu uz atsevišķām kastēm, daudz pareizāk būtu izveidot vairākas kastes ar visu aplikāciju un uzlikt loadbalanceri. Atkristu liekā starpserveru komunikācija, kas uzlbotu kā CPU performani, tā arī requesta atbildes laiku.

Bail pat iedomāties, ja kāds izdomātu šādu aplikāciju sadalīt pa 20 serveriem, kur primais serveris griežas pie 3 citiem, tie savukārt vēl pie citiem, tie savukārt vēl tālāk. Atbildes laiks šādai aplikācijai bos ļoti slikts.

Ta vietā, paņem 20 serverus, kura katram ir pilna aplikācija, kur loadbalanceris sadala, kurai kastei sūtīt pieprasījumu, kurš tad arī tiek apstrādāts tās kastes + db kastes ietvaros.

Edited by codez
Link to comment
Share on other sites

...Bail pat iedomāties, ja kāds izdomātu šādu aplikāciju sadalīt pa 20 serveriem...

Manuprāt tāda veida atdalīšana noderētu tad un tikai tad, ja kāda konkrēta aplikācijas daļa (viena MVC triāde) izpilda tādas operācijas/aprēķinus, kas pamatīgi noslogo serveri. Tad sadali aplikāciju pa 19 serveriem ar loadbalanceri un katrs no tiem serveriem slēgsies pie tā viena, kas spēs viens pats (kaut vai trīs citi uz vienu MVC triādi) paveikt visu pārējo serveru pieprasījumus.

Te gan laikam būtu nepieciešams, lai HMVC darbojas "visā web slānī".

Link to comment
Share on other sites

Ar HMVC es šajā konkrētajā gadījumā arī biju domājis to, ko tu nodemonstrēji savā atbildē - vienkāršo variantu, kurā vienas sistēmas ietvaros uz vienas kastes tiek kādā vietā izpildīts cits kontrolieris. Cik atceros laiku, kad tikai sāku apgūt MVC, ZF un pētīju citus brīnumus, tad tā lieta man vienkārši tā uzreiz netapa skaidra.

 

Tā kā tiem, kas mēģinās apgūt to pasākumu, tavs komentārs par atbilstošo iespēju realizēšanu būs pašā laikā.

Link to comment
Share on other sites

Sveiki.

 

Šobrīd būvēji bezmaksas CMS kurš būs pieejams visai WWW publikai, un tas arī ir MVC, tikai ar tādu kā update, šis saīsinājums būtu: ALPT

 

(Addons,Library,Programming,Templates)

 

Addons - Satur lapas papildinājumus (Navigācija, Paneļi, Lietotāji, Forums, Komentāri) etc.

Library - no MVC (Modeļi)

Programming - no MVC (Controllers)

Templates - no MVC (Views)

 

Darbības principi:

Tiek padota adrese piemēram: lapa.lv/forums/sarunas/51

 

1. Tiek meklēts Programming "Forums", ja tas netiek atrasts tiek meklēts Addons "forums"

2. Kad tas atrasts tiek lietota metode sarunas ar parametru 51

3. Tiek lietoti Library lai iegūtu vajadzīgos datus

4. Padod tos templates

 

Itkā nekas nav mainijies kā MVC, tikai tiek pievienots papildus kontrolieris "Addons"

 

Paskaidrošu kapēc.

 

Addons struktūra folderī:

 

/addons/system

/addons/system/main_settings

/addons/system/navigation

/addons/system/panels

/addons/users

/addons/users/manager

/addons/users/blacklist

/addons/content

/addons/content/news

/addons/content/forum

/addons/content/votes

/addons/content/images

 

Respektīvi Addons ir Kontrolieris kurš tiek sakārtots lai būtu ērtāk pārskatāms, bet tanī pašā laikā paturot iespēju veidot Programming (Parastos kontrolierus)

 

Uzskatu šo par labu variantu - tapēc cenšos ņemt vērā visus dzīvē iegūtos principus / izvērtēt tos un veidot šo ALPT pēc iespējas ērtāku programmētājam.

 

Šis Framework CMS ko grasos dot publikai strādā pēc šādiem principiem.

 

1. Tiek ielādēts objekts kurš izveido Drivers, Components, Functions

1.1. Drivers - dzinējs

1.1.1. - DB - Satur datubāzes funkcijas (query/insert/update...)

1.1.2. - URI - Satur url parametru funkcijas (full_urk,parts,count)

1.2. Components - komponeti

1.2.1. - Language - Satur valodas pieejamību (Get/Update/Insert/Change/...)

1.2.2. - Libraries - Satur Libraries ielādi/noturēšanu (Models)

1.2.3. - Templates - Satur Templateies ielādi, datu padošanu (Views)

1.2.4 - Structure - Satur sturktūras ielādi, - Structure ir tas fails kurš izvēlas kuru kontrolieri ielādēt, jeb kuru addonu izmantot.

 

Vēl nedaudz par tādu sīkumu kā adresi, visi cenšās izmantot kādu GET mainīgo, uzskatu ka tas nav vajadzīgs, jo GET vajadzētu turēt tukšu tā lai nav vislaik jāuztraucas, ka tu netīšam neizmanto path GET parametru, tapēc iesaku visiem tomēr pie URL parsēšanas izmantot $_SERVER['REQUEST_URI'];

 

HTACCESS

Options +FollowSymLinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule ^(.*)$ index.php [L,QSA]

 

Objekta faila kods

<?

class Obj {
	function __construct(){
		require D.'functions'.DS.'settings'.E;
		$this->settings = new Settings;
		require D.'functions'.DS.'drivers'.E;
		$this->drivers = new Drivers;
		require D.'functions'.DS.'components'.E;
		$this->component = new Components;
	}
}
$Obj = new Obj;
$Obj->drivers->load('uri');
$Obj->drivers->load('db');

$Obj->drivers->db->connect(
	$Obj->settings->get('database_hostname'),
	$Obj->settings->get('database_username'),
	$Obj->settings->get('database_password'),
	$Obj->settings->get('database_database')
);
$Obj->component->load('language');

define("ROOT",$Obj->settings->get('document_root'));

require D.'functions'.DS.'misc'.E;
require D.'functions'.DS.'movements'.E;
require D.'functions'.DS.'main_settings'.E;

$Obj->component->load('libraries');
$Obj->component->load('templates');
$Obj->component->load('structure');

function get_instance(){
	global $Obj;
	return $Obj;
}

?>

 

 

Pēc šī FrameWork CMS palaišanas ceru un Jūsu PHP.LV atsauci buggot un komentēt par labākiem risinājumiem, tā lai ir iespēja publikai dot pēc iespējas kvalitatīvāku FM cms.

Link to comment
Share on other sites

Manuprāt tāda veida atdalīšana noderētu tad un tikai tad, ja kāda konkrēta aplikācijas daļa (viena MVC triāde) izpilda tādas operācijas/aprēķinus, kas pamatīgi noslogo serveri. Tad sadali aplikāciju pa 19 serveriem ar loadbalanceri un katrs no tiem serveriem slēgsies pie tā viena, kas spēs viens pats (kaut vai trīs citi uz vienu MVC triādi) paveikt visu pārējo serveru pieprasījumus.

Te gan laikam būtu nepieciešams, lai HMVC darbojas "visā web slānī".

Tam būtu praktiska jēga tikai tad, ja pieprasījumi citam serverim notiktu asinhroni, jo savādāk pirmajam serverim tik un tā jāgaida kamēr otrais pabeigs savus sarežģītos aprēķinus.

Bet asinhronas sistēmas gadījumā, kam PHP īsti nav pielāgots nāktos realizēt diezgan sarežģītu asinhrono atbilžu menidžēšanu. Respektīvi, asinhronie procesi jāpalaiž pamatprocesa gaitā, bet beigās ir jāskatās vai visi asinhronie procesi ir pabeigti un atgriezuši datus, tad vēl jāveic beigu pārsēšana, lai saliktu asinhroni iegūtos datus kopā.

Manuprāt koda kompleksitāte tad pamatīgi pieaugtu.

Lai realizētu patiesi labu asinhronu HMVC, šeit prasās pēc jaunas serveru skriptu valodas, kuras pamatā jai ir daudzprocesu darbība.

 

 

Darbības principi:

Tiek padota adrese piemēram: lapa.lv/forums/sarunas/51

 

1. Tiek meklēts Programming "Forums", ja tas netiek atrasts tiek meklēts Addons "forums"

2. Kad tas atrasts tiek lietota metode sarunas ar parametru 51

Kā jau rakstīju 1. postā, tad šī ir viena no divām populārākajām routing sistēmām, bet man viņa nepārāk patīk, jo

tā vietā, lai veidotu 1 kontroleri ar vairākām metodēm, kas routotos šādi:

/forum/forums/123

/forum/topics/123-basic

/forum/topic/123-hello-world

es dodu priekšroku, katrai šai metodei taisīt savu kontroleri un routot šādi:

/forums/123

/topics/123-basic

/topic/123-hello-world

 

Praksē neesmu saskāries ar vajadzību taisīt /kontrolieris/metode/parametrs1/parametrs2 ceļa norādīšanu, vienmēr ir pieticis ar /kontrolieris/parametrs1/parametrs2

 

Respektīvi šeit es ieteiktu izveidot pamatkontrolieri, kurš darbojas pēc manis piedāvātā mehānisma:

/kontroliers/parametrs1/parametrs2/utt...

un tad paplašīnātu kontrolieri, kurš extendo pamatkontrolieri un atbilstoši parametram1 izsauc atbilstošu savu metodi.

Tādā veidā tiks apmierinātās aba veida prasības.

 

Šis Framework CMS ko grasos dot publikai strādā pēc šādiem principiem.

 

1. Tiek ielādēts objekts kurš izveido Drivers, Components, Functions

1.1. Drivers - dzinējs

1.1.1. - DB - Satur datubāzes funkcijas (query/insert/update...)

1.1.2. - URI - Satur url parametru funkcijas (full_urk,parts,count)

1.2. Components - komponeti

1.2.1. - Language - Satur valodas pieejamību (Get/Update/Insert/Change/...)

1.2.2. - Libraries - Satur Libraries ielādi/noturēšanu (Models)

1.2.3. - Templates - Satur Templateies ielādi, datu padošanu (Views)

1.2.4 - Structure - Satur sturktūras ielādi, - Structure ir tas fails kurš izvēlas kuru kontrolieri ielādēt, jeb kuru addonu izmantot.

Pāris lietas:

1)Klašu ielādi veikt ar autoload, lai tiktu lādētas tikai izmantotās klases

2)Pie inicializācijas defaultā nelādēt nevienu citu klasi kā tikai kontroleri, jo nav vajadzīgs ne db, ne daudzas citas klases, ja mums vienkārši ajax requestam, piemēram, jāpaņem dati no memcahced un jāatgriež json formātā. Tā vietā klasi ielādēt ar autoload tieši tajā brīdī, kad tā ir nepieciešama. Tādā veidā FW būs ideāli piemērots ajaxīgām aplikācijām.

3)Globālu komponenšu izmantošanai izmantot nevis globālu objektu, bet gan klasi ar staticku instance parametru un metodi, kura atgriež iznstanci. Tāpat arī factory patternu realizēt ar statiskām klases metodēm, piemērām Drivers::load('load') un Drivers::get('db')

 

Vēl labprāt redzētu, kā tev izskatās usera veidots kontroleris un kā tev tiek nodrošināts vairākiem kontroleriem kopējs headeris un footeris.

Edited by codez
Link to comment
Share on other sites

Šobrīd esmu pārdomās par objekta izmaiņām.

Kā jau visi zinat lai kautko izveidotu nepietiek ar vienu piegājienu.

 

Esmu izpētījis vairākus MVC, šobrīd pirmajā vietā noliktu CodeIgniter.

Par kontrolieru metodēm runājot, arī biju aizdomājies, varbūt tomēr neveidot klasi, bet attiecīgi veidot katram kontrolierim failu kurš tad attiecīgi sagremo ko ar viņu dara.

 

/forum/5

/topic/4

/post/51

 

 

Lūk kontrolieris "admin.php", kuram ir metode INDEX, kuru izmanto gadijumā ja nav atrastas metodes, tad tiek meklēts ADDONS, ja tiek atrasts rādam addonu.

<?php
class adminProgrammingClass {

	function __construct(){
		$instance = get_instance();
		$users = $instance->components->libraries->load('users');
		$uid = $users->authorize();
		define('UID',$uid);
		if(!$uid && $instance->drivers->uri->get(2) != 'authorization') kill("/admin/authorization");
		elseif($uid && $instance->drivers->uri->get(2) == 'authorization') kill("/admin/welcome_center");
	}

	function index($addon=''){
		if($addon){
			if($this->addonDisplay($addon)) return;
		}
		kill("/admin/welcome_center");
	}

	function addonDisplay($addon){
		$instance = get_instance();
		$module = $instance->components->libraries->load('addons');
		if($module->exists($addon)){
			if(isAjax()){
				$module->load($addon);
			}else{
				$instance->components->templates->load('header');				
				$module->load($addon);
				$instance->components->templates->load('footer');				
			}
			return true;
		}
		return false;
	}

	function authorization($username = '',$status=0){
		$instance = get_instance();
		if($_POST['username']){
			$users = $instance->components->libraries->load('users');
			if($users->validate($_POST['username'],$_POST['password'])){
				kill('/admin');
			}else{
				$username = strip_tags($_POST['username']);
				$status = 1;
			}
		}

		if(isAjax()){
			$instance->components->templates->load('authorization/first',array('username'=>$username,'status'=>$status));
		}else{
			$instance->components->templates->load('header');
			$instance->components->templates->load('authorization/first',array('username'=>$username,'status'=>$status));
			$instance->components->templates->load('footer');
		}
	}

	function welcome_center(){
		$instance = get_instance();
		if(isAjax()){
			$instance->components->templates->load('welcome_center/first');
		}else{
			$instance->components->templates->load('header');
			$instance->components->templates->load('welcome_center/first');
			$instance->components->templates->load('footer');
		}
	}

}
?>

Edited by EdgarsA
Link to comment
Share on other sites

Pēc šī FrameWork CMS palaišanas ceru un Jūsu PHP.LV atsauci (..)

Huh ?! Framework's ir klašu/funkciju kolekcija, kuras regulāri ir nepieciešamas. CMS ir web aplikācija, kas ļauj gala-jūzerim kontrolēt lapas saturu.

Ko tieši tu gribi pateik, ar "FrameWork CMS" ?

 

p.s. CMS nav priekš programētaja, bet gan priekš jūzera.

Link to comment
Share on other sites

Esmu uzklausijis dažus no Jūsu padomiem un izstrādājis strukturu pēc kuras tiks labots mans fw.

 

1. Ielādē objekta failu kurā ir checkfile

2. checkfile - parbauda vai esi ajax vai non-ajax

2.1. - Ajax gadijuma

2.1.1 - Tiek savakts config.php

2.1.2. - Tiek parbaudits vai eksiste tads kontrolieris

2.1.3. - Tiek ieslegts kontrolieris.

2.1.4. - Ja modelis ko panejm kontrolieris (Ja panjem) izmanto db, tad tur arii tiek pieslegts db

2.1.4.1 - Respektivi, ja man ir ajax ielade un es sava modeli izpildu "Db::query", tad objekts inkludo db klasi un piekonetejas un izpilda db query.

2.1.5. - Tapat ka 2.1.4 tas notiek ar memcached

2.1.6 - Nelietojam ob_ funkcijas, bet pa taisno printejam ara un beidzam skriptu ar "exit();"

2.2. Non-ajax gadijuma

2.2.1. - Tiek savakts config.php

2.2.2. - Tiek parbaudits vai eksiste tads kontrolieris

2.2.3. - Tiek pieslegts memcached un mysql

3. Tiek pieslegta funkcija ar kuru izmantojot Model/View funkcijas tas inkludotu shis klases, jo vinas nav ieklautas kamer tas netiek izmantotas.

 

 

Kautkā tā, jūsu domas par šo strukturu ?

Link to comment
Share on other sites

EdgarsA,

 

1) jau man nepatika, ka tev kontrolieros vienmēr manuāli jāizsauc Header-a un footer-a renderēšana. Labāk būtu to realizēt automātiski, kaut vai tā kā es piedāvāju, ka ir parent kontrolieris, kuru norāda un kurš sevī ievieto izsaukto kontrolieri.

 

2) Nevajag sarežģīt MVC struktūru tajā iekļaujot jēdzienu ajax. Es tā vietā daru tā, ka MVC kodolam ir vienalga, kas tas par pieprasījumu, bet man ir kontrolieris ajax uz kuru tad arī sūtu visus ajax pieprasījumus. Tālāk jau šo kontrolieri var būvēt kā grib, tas var palaist citus speciālos ajax kontrolierus vai kaut kādā savādākā veidā izpildīt esošos kontrolierus.

Respektīvi, ajax kontrolieris vienkāŗši tiek izsaukts kā: /ajax/param1/param2/utt. + arī post dati.

Es ajax kontrolieri parasti uzbūvēju tā, ka pirmais parametrs ir ajax klase kura jāizsauc, bet otrai parametrs metode, respektīvi:

/ajax/klase/metode

 

3)Neajaxīgā gadījumā arī nevajag defaultā slēgt ne memcached, ne db, jo var būt gadījums, ka lapas renderēšanai pietiek ar memcahced, tad db nevajadzēs, vai tieši otrādi, nekas nav jākešo un tiks izmantota tikai db.

Abiem šiem izmanto singletona paternu.

Ja domā, ka FW varētu būt daudz singletonu, tad var izveidot Singleton-u Factory ar __invoke maģisko funkciju. Pieņemsim, ka klasi sauks F, tad singletonus izsauks F('db')-> vai F('memcached')->

Tākā __invoke ir tikai no PHP 5.3, ko daudzi hosti vēl nav uzlikuši, tad tā vietā var izmantot vienkārši statisku metodi, tad tas izskatīsies šadi: F::F('db')->query() vai F::F('memcached')->

Edited by codez
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...