Czy dobrze rozumiem wzorzec MVC?

Czy dobrze rozumiem wzorzec MVC?
Mostek87
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 2 lata
  • Postów:228
1

Tak jak w temacie. Chciałbym się tego dowiedzieć. Przygotowałem wstępny kod aby zobrazować tok rozumowania. Proszę o ocenę i jeżeli pojawią się jakieś uwagi to było by fajnie gdyby pojawiło się również uzasadnienie. Zakładamy, że jest włączone przepisywanie adresów url w pliku .htaccess i wszystko trafia do router'a. Zakładamy, że jest to sklep odzieżowy i np pojawia się taki adres url w pasku /szukaj/kalesony/.

Kopiuj

 <?php

function autoloadClass($ClassName)  //wiadomo, automatycznie ładujemy klasy
{
  if(file_exists("php/model/$ClassName.php"))
  {
	require "php/model/$ClassName.php";
  }
  else if(file_exists("php/view/$ClassName.php"))
  {
	require "php/view/$ClassName.php";  
  }
  else if(file_exists("php/controler/$ClassName.php"))
  {
	  require "php/controler/$ClassName.php";
  }
}

 spl_autoload_register('autoloadClass');

 class Router
 {
	 public $ModelName;
	 public $ViewName;
	 public $ControlerName;
	 public $Parameter;
	 
	function __construct()  //tutaj parsujemy adres url i wyodrębniamy akcję
    {
      //jakieś działania na ciągach znaków
	  $this->ModelName = $JakasZmiennaPrzechowujacaNazwe;
	  $this->ViewName = $JakasZmiennaPrzechowujacaNazwe;
	  $this->ControlerName = $JakasZmiennaPrzechowujacaNazwe;
	  $this->Parameter = $TuPewnieJakasTablica
	}	
 }
 
 class SearchByItemModel //klasa modelu
 {
	private $Item;
    
	public function SetItem($Item)
	{
	  $this->Item = $Item;
	}
	
	public function FindItemInDataBase()
	{
	  //łączenie z bazą danych, pobieranie danych itp cała logika tu pracuje
	  return $JakasZmiennaZapewneTablica;
	}
    	
 }
 
 class SearchByItemView
 {
   private $ItemFinder;
	 
   function __construct($ItemFinder)
   {
     $this->ItemFinder = $ItemFinder;
   }

   function Render()
   {
     //tutaj wyświetlane jest wszystko co potrzebne, tutaj jest bezpośredni dostęp do modelu
   }   
 }
 
 class SearchByItemControler
 {
	private $Model;

    function __construct($Model)
    {
      $this->Model = $Model;
	}

    function Search($Item)
	{
     $this->Model->SetItem($Item);
	}	
 }
 
 //tworzymy instancje naszych obiektów;
 
 $router = new Router();
 $model = new $router->ModelName();
 $controler = new $router->ControlerName($model);
 $parameter = $router->Parameter;
 
 $controler->Search($Parameter);
 $view = new SearchByItemView($model);
 $view->render();
 
 

?>

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
1

Zanim zabierzesz się za pisanie własnego MVC od zera, rzuć okiem np. na Laravela i to, jak jego twórcy podeszli do tematu - da Ci to dobre rozeznanie w kwestii podziału oraz nazewnictwa w PHP (którego w obecnej formie nie przestrzegasz).


edytowany 1x, ostatnio: Patryk27
Mostek87
Ale co konkretnie masz na myśli?
TR
  • Rejestracja:ponad 7 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:700m n.p.m.
  • Postów:677
0

To co sobie wymyslił twórca Laravela nie jest żadnych wyznacznikiem czy standardem.

Bardziej niż Laravelem zajmij się przestrzeganiem wzorców projektowych takich jak SOLID czy DDD. To co masz wyżej to pomieszanie MVC z DDD. Poczytaj o tym.

Jeżeli już obrałeś jakieś nazewnictwo czy strukturę która Ci pasuje, najważniejsze to ściśle się jej trzymać - to powoduje szereg ułatwień od automatyzacji refaktoringu po generalny porządek w kodzie.


DRY > SOLID (nie bierz tego zbyt poważnie)
edytowany 3x, ostatnio: TomRZ
Zobacz pozostałe 13 komentarzy
Patryk27
Wstukaj DDD w Google - wszystkie odnalezione przeze mnie definicje mówią podejście do tworzenia oprogramowania, a żadna nie używa słowa wzorzec.
TR
To teraz wstukaj w google "wzorzec" i zobacz jaka jest definicja, DDD praktycznie jest wzorcem tylko bardzo rozszerzonym. Ok ja nie mam zamiaru tracić więcej czasu bo ta dyskusja nigdzie nia prowadzi. Nara.
PA
słowo, które może oznaczać wzór jednostki miary, wzór rzeczy, wyrobu albo zalecany wygląd lub zachowanie się osoby (wzór do naśladowania) lub zwierzęcia. No wstukałem. Co to ma wspólnego wzór do wytwarzania oprogramowania? czy w DDD wzorujesz się na czymś? Faktycznie idź lepiej stąd bo się kompromitujesz.
LukeJL
myślę, że jeśli chodzi o poglądy, to każdy ma prawo mieć swoje (ja bym nie nazwał DDD wzorcem, raczej pewną filozofią, która korzysta z wzorców jak narzędzi). Z drugiej strony jeśli ktoś się kłóci z kimś, że on ma rację i "moja racja jest najmojsza", to wypadałoby podać bardziej merytoryczne argumenty niż "co za herezje", "nie wierz we wszytko co pisze na wikipedii" albo naginanie logiki do swoich celów (najpierw przykłady o bułkach, a potem "to co piszę wynika wprost z logiki". No nie).
LukeJL
z drugiej strony dla mnie bardzo dużo elementów takich bardzo podstawowych jest wzorcem. Nawet jak ktoś pisze klasę, to w pewnym sensie stosuje wzorzec projektowy pod tytułem klasa (może dlatego, że programuję w JS, w którym przez jakiś czas w ogóle nie było klas, więc łatwo mi postrzegać klasy jako design pattern niż jakbym miał programować w języku klasowym, w którym klasy są jak powietrze).
Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
3

@Mostek87:

  1. Nie pisz własnego autoloadera - wykorzystaj Composera (Composer ma dwie funkcjonalności: zarządzanie paczkami oraz budowanie autoloadera - Tobie potrzebna jest głównie ta druga).

  2. protected $foo; (a nie protected $Foo;; to samo odnośnie private czy public - widoczność określiłem tylko na rzecz przykładu; taka jest konwencja języka).

  3. function doSomething(), a nie function DoSomething() (taka jest konwencja języka).

  4. Nie modelName, tylko modelClassName itd. (niby niewielka różnica, ale widząc modelName możesz zacząć się zastanawiać, czy znajduje się tam ścieżka do pliku, czy może coś innego - a modelClassName jest dosyć jednoznaczne).

Ogólnie nie ma źle - rzucam się głównie o stylistykę i nazewnictwo, ale są to kwestie dosyć istotne ;-)

Sam koncept wydaje mi się, że rozumiesz, chociaż mógłbyś podać jakiś bardziej wymagający przykład (np. ogólny zarys kodu zarządzającego postami w bazie danych bloga).


edytowany 2x, ostatnio: Patryk27
TR
  • Rejestracja:ponad 7 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:700m n.p.m.
  • Postów:677
1

To co wyżej napisał Patryk to znowu nie jest żaden standard ani wyznacznik.

Możesz robić tak jak robisz "kamelizująć" już od pierwszej litery - tyle, że bądź w tym konsekwentny.

Jeżeli chcesz to równie dobrze Twoje zmienne i fukcje mogą wyglądać tak:

public function do_something, protected $model_name etc - to jest trochę starszy ale nadal bardzo dobry sposób nazewnictwa, który część ludzi preferuje ponad kamelizację.


DRY > SOLID (nie bierz tego zbyt poważnie)
edytowany 2x, ostatnio: TomRZ
Zobacz pozostałe 2 komentarze
Patryk27
PSRy są w mniejszej lub większej części uznane wśród większości społeczności i praktycznie każdym frameworku - nawet jeśli nie są standardem wprost, są de facto standardem i trzymanie się ich przyniesie z całą pewnością więcej pożytku niż zła.
TR
Co do nazewnictwa jestem w skłonny się zgodzić, podobnie sposobu ładowania klas, co do pozostałych rzeczy które rekomenduje PSR już tak nie jest. Natomiast to trochę sztuczny problem, jak już wspominałem - najbardziej w nazewnictwie liczy się konsekwencja a nie sposób, używanie konstrukcji nazwa_zmiennej nie stanowi problemu jeżeli wszędzie ktoś tak robi w swoim kawałku kodu / bibliotece.
Patryk27
Tak - a potem łączysz kilka bibliotek czyjegoś autorstwa i piszesz $bibliotekaA->something_do($bibliotekaB->makeOBJECT(true))->then_and($bibliotekaC->fetch_____object(false));, bo każdy postanowił robić po swojemu (ale grunt, że konsekwentnie w obrębie własnego kodu!). Nazewnictwo jest istotne - nie tylko trzymanie się jednolitego standardu, ale również pisanie tak, jak reszta. Nie bez powodu niektóre języki wręcz narzucają standardy (np. Go).
TR
Tyle, że PHP nie jest takim językiem, to nie Java. Twój przykład jest jakimś absurdem, w dobie zaawansowanych IDE nazewnictwo to najmniejeszy problem. Kiepski algorytm nawet idealnie zgodny z PSR będzie gorszy od lepszego algorytmu bez względu na nazewnictwo.
Patryk27
Ad 1: w jakim sensie PHP nie jest takim językiem jak Java? Ad 2: ależ oczywiście, że lepiej mieć kod, który działa od takiego, który nie jest przydatny w ogóle. Byłem przekonany, że pomijamy takie podstawowe założenia.
Mostek87
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 2 lata
  • Postów:228
0

Super, dzięki bardzo za uwagi. Faktycznie co do stylistyki powinienem co najmniej być konsekwentny - czego u mnie zabrakło. Sam się na tym przyłapałem, ale szczerze mówiąc zwyczajnie z lenistwa nie chciało mi się poprawiać ;) Teraz i tak muszę poprawić cały kod - popełniłem błąd i kontroler najpierw pobierał dane z modelu a potem przekazywał je widokowi. Co ciekawe w sieci jest pełno tutoriali, które błędnie stwierdzają, że tak właśnie powinno być. Jak się potem okazało w MVC widok ma bezpośredni dostęp do modelu. Polecam dwa artykuły, które mi pomogły

https://r.je/views-are-not-templates.html
https://r.je/mvc-tutorial-real-application-example.html

Ja czmycham poprawiać kod, jak skończę to wrzucę już bardziej praktyczny przykład do oceny.

Mostek87
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 2 lata
  • Postów:228
0

Poprawiłem kod swej aplikacji. Jeśli komuś będzie się chciało analizować moje wypociny to właśnie one:
Router

Kopiuj
function autoloadClass($className) //no dobra wiem, że composer byłby lepszy ale póki co nie chce mi się tego ogarniać :p
{
  if(file_exists("php/model/$className.php"))
  {
	require "php/model/$className.php";
  }
  else if(file_exists("php/view/$className.php"))
  {
	require "php/view/$className.php";  
  }
  else if(file_exists("php/controler/$className.php"))
  {
	 require "php/controler/$className.php"; 
  }
  else if(file_exists("php/extra/$className.php"))
  {
	 require "php/extra/$className.php";   
  }
}

spl_autoload_register('autoloadClass');
 
class Router
{
  public $parameters = null;
  public $modelName;
  public $viewName;
  public $controlerName;
  
  private function getClassName(&$action)
  {
	$classes = array(
    "kategorie"	=> "Categories" );
	
	return $classes[$action];
  }
  
  function __construct()
  {
	    $arr = explode("/",$_SERVER['REQUEST_URI']);      //tu wyodrębniam sekcję jaką chce odwiedzić użytkownik
		$userAction = $arr[1];
		$userAction = ($userAction === "" || $userAction === "strona") ? "MainPage" : $this->getClassName($userAction); 
		
		$arrLength = count($arr);

		$this->modelName = $userAction."Model";
		$this->viewName = $userAction."View";
		$this->controlerName = $userAction."Controler";
		
		if($arrLength > 3)
		{
		    $this->parameters = array();  //tu będę pakował parametry - 
  		
			for($i = 2; $i < $arrLength - 1; ++$i)
            {
              array_push($this->parameters, $arr[$i]);  //ładuję parametry do tablicy aby potem przekazać je do kontrolera
			}		
				
		} 
  }
    
}

$router = new Router;
$model = new $router->modelName();
$controler = new $router->controlerName($model);
$controler->initiate($router->parameters);
$view = new $router->viewName($model);
$view->render();

klasa abstrakcyjna kontrolera

Kopiuj
abstract class Controler
{
  private $model;
  abstract function initiate($parameters);
}

kontroler dla strony głównej

Kopiuj

class MainPageControler extends Controler 
{
	function __construct($model)
	{
	  $this->model = $model;	
	}
	
	function initiate($parameters)
	{
	  $pageNumber = (empty($parameters)) ? 1 : $parameters[0];
      $this->model->currentPage = Intval($pageNumber);	  
	}
}

model dla strony głównej

Kopiuj
class MainPageModel
{
	public $currentPage;
  
    private function createQuery()  //w zależności od numeru strony tworzę odpowiednie zapytanie do bazy danych
   {
	  $pageNumber = $this->currentPage - 1;
		
	  if($pageNumber == 0)
	  {
		return "SELECT * FROM Pornusy ORDER BY klucz DESC LIMIT 50";   
	  }
	  else
	  {
		$offset = $pageNumber * 50;
        return "SELECT * FROM Pornusy ORDER BY klucz DESC LIMIT 50 OFFSET $offset";		
	  }
	    
	}
	
	private function getRandomImages()
	{
	    $id1 = rand(1,20);   //wybieram dwa losowo wybrane zdjęcia
        $id2 = rand(1,20);

        while($id1 === $id2)
	   {
	     $id1 = rand(1,20);
            $id2 = rand(1,20);	
	    }
	
	   $this->randomImage1 = "decoration-image-".$id1;
	   $this->randomImage2 = "decoration-image-".$id2;	
	}
	
	private function calculatePageControls(&$total)   //obliczam wartości dla kontrolek sterujących podstronami
	{
	  $maxPageIndex = ceil($total / 50);
	  $pageNumber = &$this->currentPage;
	  $pageControls = new PageControls();
	  
      if($maxPageIndex - 5 >= $pageNumber)
      {
        $pageControls->pageControlIndex1 = $pageNumber;
		$pageControls->activePagePosition = 1;
	  }
      else
	  {
        $pageControls->pageControlIndex1 = $maxPageIndex - 4;
		$pageControls->activePagePosition = 5 + $pageNumber - $maxPageIndex;
	  }
	  
      $pageControls->topIndex = ++$total - (--$pageNumber * 50); 
      $pageControls->maxPageIndex = $maxPageIndex;
	  
	  $id1 = rand(1,20);   //wybieram dwa losowo wybrane zdjęcia
      $id2 = rand(1,20);

        while($id1 === $id2)
	   {
	     $id1 = rand(1,20);
         $id2 = rand(1,20);	
	    }
	
	   $pageControls->randomImage1 = "decoration-image-".$id1;
	   $pageControls->randomImage2 = "decoration-image-".$id2;
	  
	  return $pageControls;
    	  
	}
	
	function getMoviesData()
	{
	  	$dataBase = mysqli_connect();
	  
	    if(!$dataBase)
	    {
		  return null;
	    }
		else
		{
		  $result = mysqli_query($dataBase,'SELECT COUNT(*) as total FROM Pornusy');
		  $row = mysqli_fetch_assoc($result);
		  $totalMovies = $row['total'];
		  mysqli_query($dataBase,"SET CHARSET utf8");
		  $query = $this->createQuery();
		  $result = mysqli_query($dataBase, $query);
		  $movies = array();
		  
		  while($row = mysqli_fetch_assoc($result))
		  {
			array_push($movies, new Movie($row['opis'], $row['gwiazdy'],$row['wyswietlenia'], $row['sekundy'],$row['minuty'],$row['godziny'], $row['lektorpl']));
		  }
		  
		  mysqli_close($dataBase);
		  $pageControls = $this->calculatePageControls($totalMovies);
		  return new PagePackage($movies, $pageControls);
		  
		}
	}
}

no i widok

Kopiuj

class MainPageView extends View
 {
	 private $dataPackage;
	 
	function __construct($model)
	{
		$this->model = $model;
	}
	
	function render()
	{
	  if(file_exists("php/template/MainPage.php"))
	  {
		$this->dataPackage = $this->model->getMoviesData();
		require "php/template/MainPage.php"; 
	  }
	}
	
	private function createBackForwardControls()              //wyświetlanie kontrolek sterujących podstronami
	{
	  $pageControls = &$this->dataPackage->pageControls;
	  $LeftControlNumber = &$pageControls->PageControlIndex1;
	  $MaxPageIndex = &$pageControls->MaxPageIndex;
	  $ActivePagePosition = &$pageControls->CurrentPage;
	  
	     if($LeftControlNumber > 1)
        {
		  $PreviousPageNumber = $ActivePagePosition - 1;
          $LeftAnchor = 'href="/strona/'.$PreviousPageNumber.'/"';
	    }
        else
        {
          $LeftAnchor = "";
	    }

        if($ActivePagePosition >= $MaxPageIndex)
        {
          $RightAnchor = "";
		}
        else
		{
          $NextPageNumber = $ActivePagePosition + 1;
		  $RightAnchor = 'href="/strona/'.$NextPageNumber.'/"';
		}
    
	     echo '<li class="controls-element controls-element-bottom">
          <a '.$LeftAnchor.' class="page-selection page-selection-second-layer">wstecz</a>
          </li>';
   
        echo '<li class="controls-element controls-element-bottom">
          <a '.$RightAnchor.' class="page-selection page-selection-second-layer">dalej</a>
        </li>';
		
	}
	
	 function createPageNumbers()  //wyświetlanie kontrolek sterujących podstronami
	{
	 $pageControls = &$this->dataPackage->pageControls;
	 $LeftNumber = &$pageControls->pageControlIndex1;
     $ActiveControl = &$pageControls->activePagePosition;
	 
		for($I = 0; $I < 5; ++$I)
		{
			$Class = ($I + 1 == $ActiveControl) ? "active" : "";
			$PageNumber = $I + $LeftNumber;
			
			echo '<li class="controls-element">
                   <a href="/strona/'.$PageNumber.'/" id="PageControl-'.$I.'" class="page-selection page-selection-first-layer '.$Class.'">'.$PageNumber.'</a>
                 </li>';
		}
	}
	
	private function createTimeLabel($movie)   //tworzenie etykiety z czasem trwania filmu np. 00:34
	{
	  
	  $seconds = &$movie->seconds;
	  $hours = &$movie->hours;
      $minutes = &$movie->minutes;
	  
	  $seconds = $seconds < 10 ? "0$seconds" : $seconds;
	  $hours = $hours != null ? "0$hours:" : "";
	  
      if($minutes == null)
	  {
         $minutes = "00:";	
	  }
      else
      {
        $minutes = ($minutes < 10) ? "0$minutes:" : "$minutes:";
	  }  
	  
	  return $hours.$minutes.$seconds;
	}
	
	function showMovies()  //wyświetlanie filmów
	{
		$currentID = &$this->dataPackage->pageControls->topIndex;
		
		foreach($this->dataPackage->moviesData as $movie)
		{
			$time = $this->createTimeLabel($movie);
			$description = &$movie->tittle;
			$views = &$movie->views;
			$stars = &$movie->stars;
			
			if($stars != null)
			{
			  $stars = 'data-stars="'.$stars.'"';	
			}
			else
			{
			  $stars = "";	
			}
			
		   echo '<div class="movie-complete">
		       <div class="image-and-duration"><img class="movie-small-image" src="/images/movie/main/'.$currentID.'.jpg" /><time class="duration">'.$time.'</time></div>
			   <div class="movie-description">'.$description.'</div>
			      <div class="additional-options">
					 <span class="views">'.$views.' odsłon</span>
					 <img alt="podgląd filmu" title="kiliknij aby podejrzeć film" class="magnifier-icon" '.$stars.' data-id="'.$currentID.'" src="/images/controls/magnifier.png"/>
			      </div>
	      </div>';	
		  
		  --$currentID;
		}
	}
 }

TR
  • Rejestracja:ponad 7 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:700m n.p.m.
  • Postów:677
0

Przepraszam nie analizowałem wszystkiego z braku czasu, chciałbym jedynie zwrócić uwagę na umiejscowienie klas.

Popełniasz moim zdaniem tutaj błąd, który niestety masowo jest powielany, można go też zobaczyć w większości frameworków.

Błąd polega na przydzielaniau osobnych katalogów na klasy widoków, klasy kontrolerów, i inne.

Może się wydawać, że zrobienie czegoś takiego ułatwia programowanie, ale to jest złudne, na dłuższą metę utrudnia.

  • stosująć taki autoloading musisz sprawdzać kilka katalogów

  • kiedy chciałbyś tworzyć aplikację z części lub modułów, te modułu muszą być dostosowane do takiej struktury katalogów

  • różne frameworki mają różne struktury katalogów, dlatego najlepsze co można zrobić w celu poprawienia przenosności jest maksymalne uproszczenie tej kwestii bez straty np. na bezpieczeństwie

Takim wyjściem jest określenie jednego jedynego katalogu który zawiera klasy, to może być np. katalog class, i ładowania klas wg. namespaces i nazwy klasy.

Np. klasa \myshop\controller\CartController, czyli kasa CartController znajdująca się w przestrzeni nazw myshop\controller, będzie mieć ścieżkę: class\myshop\controller\CartController.php, a klasa \myothermodule\dupamaryna\CheckPrivileges będzie mieć ścieżkę class\myothermodule\dupamaryna\CheckPrivileges

To jest bardzo proste i elastyczne.

Nie trzeba mieć specjalnego osobnego katalogu na kotroler, w routingu po prostu określa się pełną ścieżkę do klasy, podobnie z innymi sprawami. To bardzo elastyczne i dające dużo wolności rozwiązanie w duchu PHP.


DRY > SOLID (nie bierz tego zbyt poważnie)
edytowany 3x, ostatnio: TomRZ
PA
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 6 lat
  • Postów:339
0
TomRZ napisał(a):

Przepraszam nie analizowałem wszystkiego z braku czasu, chciałbym jedynie zwrócić uwagę na umiejscowienie klas.

Popełniasz moim zdaniem tutaj błąd, który niestety masowo jest powielany, można go też zobaczyć w większości frameworków.

Błąd polega na przydzielaniau osobnych katalogów na klasy widoków, klasy kontrolerów, i inne.

Może się wydawać, że zrobienie czegoś takiego ułatwia programowanie, ale to jest złudne, na dłuższą metę utrudnia.

  • stosująć taki autoloading musisz sprawdzać kilka katalogów

Co Ty znowu bredziesz człowieku ? Co musi sprawdzać? każesz mu robić syf w katalogach pchając wszystko w jeden folder a potem szukanie jednego pliku wśród widoków/modelów i innych?
Wystarczy użyć psr-4 przez composera i może mieć nawet 200 folderów z podfolderami a autoloading i tak będzie działał.

TR
  • Rejestracja:ponad 7 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:700m n.p.m.
  • Postów:677
0

Wypraszam sobie do mnie teksty w stylu "co Ty bredzisz". Jeżeli nie umiesz się zachować, to wyjdź.

I jeszcze jedno: w PSR4 nie ma nic na temat struktury katalogów która rozdziela osobno kotrolery i inne rzeczy, może najpierw poczytaj zanim czymś się podeprzesz.


DRY > SOLID (nie bierz tego zbyt poważnie)
edytowany 3x, ostatnio: TomRZ
PA
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 6 lat
  • Postów:339
0

A czy ty kiedykolwiek widzialeś narzędzie które oparte jest o nazewnictwo katalogów ? Drugi raz wypowiadasz się na temat PHP i drugi raz nie wiesz o czym piszesz.

TR
  • Rejestracja:ponad 7 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:700m n.p.m.
  • Postów:677
0

Człowieku, umówmy się, że Twoja opinia nic dla mnie nie znaczy. Ja się tutaj wypowiadam na temat, a Ty mnie atakujesz i zasyfiasz wątek, idź być Trollem gdzie indziej.


DRY > SOLID (nie bierz tego zbyt poważnie)
KL
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:191
0

A to, jeśli już chcemy porzucić klasyczny, wydawałoby się (mi) sposób rozdziału na modele, widoki i kontrolery, to nie lepiej by było trzymać pliki w folderach, które będą odpowiadały modułom?
Wtedy jak chcesz użyć, dajmy na to, swojej autoryzacji w następnym projekcie, to łapiesz katalog, a nie szukasz tych czterech plików wśród całej masy innych.

I akurat tutaj wrzucenie wszystkiego na pałę(jeśli taki miał być przekaz) do "classes", bez żadnej wewnętrznej jego struktury faktycznie powoduje bajzel - nie wyobrażam sobie przeszukiwania kopy plików w poszukiwaniu tego jednego, którego potrzebuję.

I tak samo co do loadingu - nigdy nie zauważyłem żeby użycie dowolnie głęboko zagnieżdżonej struktury powodowało jakiekolwiek problemy, dopóty, dopóki była ona spójna. Zatem trzymanie wszystkiego w jednym miejscu traci argument o problematyczności rozwiązania, a niestety, jak wyżej wspomniałem, powoduje problem z odnajdywaniem potrzebnych rzeczy.

Inna rzecz, że z PHP od kilku lat się praktycznie nie widziałem nawet, więc cóż...

edytowany 1x, ostatnio: Klojtex
TR
  • Rejestracja:ponad 7 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:700m n.p.m.
  • Postów:677
0

I akurat tutaj wrzucenie wszystkiego na pałę(jeśli taki miał być przekaz) do "classes", bez żadnej wewnętrznej jego struktury faktycznie powoduje bajzel - nie wyobrażam sobie przeszukiwania kopy plików w poszukiwaniu tego jednego, którego potrzebuję.

Czy jesteś w stanie przedstawić jakiś konkretny przykład kiedy musiałbyś czegoś szukać?

Nie rozumiem gdzie i po co miałoby być to szukanie plików? Przecież takie podejście z jednym katalogiem nie likwiduje podziału MVC, powiem więcej: czyni je bardziej elastycznym.

Posiadając jeden katalog na klasy bardzo łatwo tworzyć elastyczne moduły.

Autoryzacja: autoryzacji nie robi się przez katalogi tylko przez logikę aplikacji która wie do czego ma dostęp użytkownik a do czego nie. Listy ACL i tym podobne sprawy, katalogi nie mają tu nic do rzeczy, poziom katalogów to poziom konfiguracji serwera WWW - głównie podział na katalog(i) publicznie dostepne i publicznie niedostępne.

I tak samo co do loadingu - nigdy nie zauważyłem żeby użycie dowolnie głęboko zagnieżdżonej struktury powodowało jakiekolwiek problemy, dopóty, dopóki była ona spójna. Zatem trzymanie wszystkiego w jednym miejscu traci argument o problematyczności rozwiązania, a niestety, jak wyżej wspomniałem, powoduje problem z odnajdywaniem potrzebnych rzeczy.

Naprawdę tego nie kapuje, jeżeli masz klasę to od razu automatycznie wiesz gdzie jest - po samej nazwie! Tak jak to wyjaśniałm wcześniej, prościej się nie da. Tymczasem przy kilku różnych katalogach tak naprawdę nie wiesz w którym katalogu jest określona klasa i wtedy właśnie musisz czegoś szukać!


DRY > SOLID (nie bierz tego zbyt poważnie)
edytowany 3x, ostatnio: TomRZ
KL
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:191
0

Zgadzam się co do kwestii rozbicia pod tym względem, że musisz otworzyć folder/katalog "controller(-s)", żeby się dostać do kontrolera, którego potrzebujesz. Natomiast mi osobiście jest zwyczajnie łatwiej stosować podział, właśnie ze względu na to, że mogę sobie niepotrzebne aktualnie pliki "ukryć" w drzewku edytora przez zwinięcie katalogu, który je zawiera. Likwiduje to "szum".

I tak, z jednej strony masz je w jednym miejscu, więc unika kilku dodatkowych kliknięć, żeby się do potrzebnego pliku dostać. Z drugiej jednak strony, gdy masz, dajmy na to 40-60 różnych plików, to powstaje właśnie powyższy "szum" - nie masz nawet najmniejszego filtrowania komponentów, które przeglądasz, więc musisz wzrokowo przelecieć przez wszystko, co się znajduje w okolicy. I jest to przykład mocno hiperboliczny, bo przy takiej ilości klas projekt jest albo naprawdę spory, albo tworzysz Planetę opierając ją na Plutonie... Czyli masz znacznie poważniejszy problem.

Co do kwestii "MVC a struktura katalogów", to nawet nie mamy co dyskutować. Oczywistym jest, że fizyczny podział tutaj nie ma za dużo do rzeczy i klasy muszą przede wszystkim byc podzielone logicznie. Prawda.
Natomiast już co do ew. przenośności kodu, to zwyczajnie chciałem zwrócić uwagę, że w razie potrzeby wygodniej jest skopiować cały fizyczny katalog wraz z jego strukturą wewnętrzną, zawierający dany moduł, aniżeli szukać pojedynczych plików. Jak chciałbym skopiować fragment odpowiedzialny za obliczanie faktur, to chcę złapać za "Invoice", a nie przeciepywać "Invoice", "InvoiceController", "InvoiceEvents", "InvoiceView" itp.

To jednak nie zmienia faktu, że powyższy post był jedynie sugestią do rozpatrzenia, a nie wielkim objawieniem. Trochę za mody i głupi jeszcze jestem na forsowanie swoich urojeń :P

drorat1
  • Rejestracja:ponad 15 lat
  • Ostatnio:około 2 lata
  • Lokalizacja:Krasnystaw
  • Postów:1181
2

Na co dzień mam do czynienia z czymś takim:

Prosty autoload oparty na nazwach i położeniu plików i PSR-0, nie ma tu żadnych namespaces

index.php w głównym katalogu projektu:

Kopiuj
define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR);

function autoload($class, $directory = 'classes')
{
    $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';

    $path = DOCROOT . $directory . DIRECTORY_SEPARATOR . $file;
    if (realpath($path))
    {
        require $path;
    }
}

spl_autoload_register('autoload');

$controller = new Controller_User;
echo $controller->action_load();

Według tego założenia, to wszystkie klasy mają być w podkatalogu classes i tak kontrolery w podkatalogu Controller, modele w podkatalogu Model oczywiście w classes. Liczą się duże i małe litery.

Kontroler:

Kopiuj
defined('DOCROOT') OR die('No direct script access.');

class Controller_User
{
     public function action_load()
     {
        $user = new stdClass;
        $user->name = 'John';
        $user->surname = 'Doe';
        $user->age = 40;

        return View::make('template', compact('user'))
            ->render();
     }
}

Klasa View do renderowania szablonu:

Kopiuj
defined('DOCROOT') OR die('No direct script access.');

class View
{
    private $_file;
    private $_data;

    public static function make($file, array $data = array())
    {
        return new View($file, $data);
    }

    public function __construct($file, array $data = array())
    {
        $this->_file = $file;
        $this->_data = $data;
    }

    public function render()
    {
        extract($this->_data);
        ob_start();
        include DOCROOT . 'views' . DIRECTORY_SEPARATOR . $this->_file . '.php';
        return ob_get_clean();
    }
}

i renderowana templatka:

Kopiuj
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <ul style="list-style: none">
            <li>Name: <strong><?= $user->name ?></strong></li>
            <li>Surname: <strong><?= $user->surname ?></strong></li>
            <li>Age: <strong><?= $user->age ?></strong></li>
        </ul>
    </body>
</html>

Struktura katalogów i plików wg. takiego założenia:

Kopiuj
classes/
    Controller/
        User.php
    Model/
        User.php        
    View.php
views/
    template.php
index.php  

Prosta implementacja MVP, jako model w tym przypadku jest stdClass. Tutaj sobie mogę tworzyć w katalogu classes różne podkatalogi jak Controller, Model, Middleware, Service, co oczywiście jest związane z odpowiedzialnością danej klasy ale każda musi mieć prefix taki jak nazwa podkatalogu. Proste jak drut. A to że teraz w nowoczesnych frameworkach jest inaczej to już inna sprawa.

edytowany 2x, ostatnio: drorat1
Mostek87
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 2 lata
  • Postów:228
0

Widzę ładnie się rozkręciliście w temacie struktury katalogów. Może jakieś uwagi na temat samego kodu? No w sumie najważniejsze jest to czy kod przestrzega reguł wzorca mvc.

drorat1
To zapoznaj się bliżej z dowolnym frameworkiem PHP i będzie z głowy. Tam jest realizacja praktyczna, to co przedstawiłem to tylko taki ogólny zarys.
Mostek87
Podobno nawet we frameworkach są błędne założenia co do mvc.
drorat1
Są jak najbardziej prawidłowe i praktyczne. A dla ścisłości masz MVP czyli pochodną MVC.
TR
  • Rejestracja:ponad 7 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:700m n.p.m.
  • Postów:677
0

Natomiast już co do ew. przenośności kodu, to zwyczajnie chciałem zwrócić uwagę, że w razie potrzeby wygodniej jest skopiować cały fizyczny katalog wraz z jego strukturą wewnętrzną, zawierający dany moduł, aniżeli szukać pojedynczych plików. Jak chciałbym skopiować fragment odpowiedzialny za obliczanie faktur, to chcę złapać za "Invoice", a nie przeciepywać "Invoice", "InvoiceController", "InvoiceEvents", "InvoiceView" itp.

Przy rozwiązaniu z jednym katalogiem, dane namespace to jest moduł - to jest właściwie logiczna konsekwencja takiego podejścia.

Kontynuując Twój przykład, cały moduł faktur byłby w namespace np. \mycompany\invoice

Jeżeli chcesz cały moduł kopiować - kopiujesz po prostu jeden caly katalog, zwróć uwagę jaką osiągasz dzięki temu przenośność - w przykładzie z wieloma katalogami musisz kopiować jeden moduł rozproszony na wiele katalogów.

W przypadku mojego podejścia - jest jeden katalog na klasy, plus jeden publiczny i jeden na zasoby takie jak szablony.

W każdym z tych katalogów utrzymujesz namespace, czyli szablon dla pokazania Invoice będzie miał ścieżkę np. resources/mycompany/invoice/show-invoice.phtml, jakiś a skrypt javascript powiązany z tym modułem będzie w public/mycompany/invoice/myscript.js

Takie podejście rozwiązuje przy okazji konflikty nazw z marszu.

Rozwiązanie które proponuje nie jest najlepsze i idealne, ale czy takie istnieje? Dzieląc na katalogi i robiąć sztywną strukturę też coś tracimy, ja chciałem Wam przedstawić coś innego, inne podejście aby poszerzyć spektrum wyboru.

Do OP wątku: to na jaką się strukturę zdecydujesz to dość podstawowa sprawa, taki szkielet który później niewygodnie zmieniać, wydaje mi się, że ta dyskusja w tym sensie może być dla Ciebie wartościowa

Edit: tutaj jeszcze classloader który ładuje wg. namespaces jeżeli katalog vendor na klasy jest w tym samym katalogu co classloader:

Kopiuj
<?php
/////////////////////////
// Classloader
spl_autoload_register( function ($className) {
  // Trying namespace...
  $className = ltrim($className, '\\');
  $fileName  = '';
  $namespace = '';
  if ($lastNsPos = strripos($className, '\\')) {
      $namespace = substr($className, 0, $lastNsPos);
      $className = substr($className, $lastNsPos + 1);
      $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
  }
  $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
  
  // Final filename
  $fileName =  __DIR__.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.$fileName;  
  
  if (file_exists( $fileName )) {
      require_once $fileName;
  }
} );

DRY > SOLID (nie bierz tego zbyt poważnie)
edytowany 4x, ostatnio: TomRZ
PA
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 6 lat
  • Postów:339
0
TomRZ napisał(a):

Człowieku, umówmy się, że Twoja opinia nic dla mnie nie znaczy. Ja się tutaj wypowiadam na temat, a Ty mnie atakujesz i zasyfiasz wątek, idź być Trollem gdzie indziej.

Trolem? tobie brakuje elementarnej wiedzy. Zastałeś się kilka lat w tył i próbujesz na forum udawać alfę i omegę mimo, że spora część osób zwraca się uwagę, że piszesz głupoty..

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)