Sklep internetowy - sposób

0

Witam. Chciałbym się Państwa zapytać, w jaki najlepiej sposób tworzyć sklep internetowy aby był on zgodny ze obecnymi wymogami (czystość kodu). Jestem początkującym programistą back-endu.
Potrafię programować strukturalnie, uczę się obiektowo, ogarnąłem na stronie już Php data object i potrafię się nim sprawnie posługiwać, z czego jestem zadowolony, lecz niedosyt pozostaje. Zastanawia mnie i jednocześnie przeraża(gdyż byłaby to pierwsza taka większa styczność z obiektówką) fakt wykorzystania MVC w tym projekcie, lecz nie wiem czy to dobry pomysł, dlaczego? sklepy z reguły mają dużą ilość podstron.

Jak programowałem do tej pory?
Tworzyłem dokumenty.php(z czystym kodem php i zapytaniami do bazy) o nazwach odzwierciedlających ich funkcję, po czym podpinałem je do dokumentów widoku poprzez include lub require.

Chciałbym także zadać jedno pytanie :
1.MVC - czy jest jakiś jedyny wzorzec, który wkleja się do kodu i pod niego pisze już swój program? czy ten wzorzec trzeba pisać samemu?

0

Jak dla mnie, metoda "hard way" jest najlepszą metodą nauki. Zajrzyj tutaj: https://www.sitepoint.com/the-mvc-pattern-and-php-1/
Artykuł, który pomoże Ci napisać własny, prosty framework (w PHP) na bazie MVC. Myślę, że dzięki temu całe zagadnienie zrozumiesz lepiej, a po jakimś czasie uznasz, że nie da się bez tego żyć.

Mnie przerażałoby bardziej tworzenie systemu webowego bez wykorzystania choćby MVC. ;)

Jak programowałem do tej pory?
Tworzyłem dokumenty.php(z czystym kodem php i zapytaniami do bazy) o nazwach odzwierciedlających ich funkcję, po czym podpinałem je do dokumentów widoku poprzez include lub require.

No, to solidny start masz już za sobą. ;)

Btw. Nie jestem pewien, czy właściwie rozumiesz pojęcie wzorca projektowego. Wzorzec to bardziej pomysł/podejście do problemu niż sam kod: https://pl.wikipedia.org/wiki/Wzorzec_projektowy_(informatyka)

0

Tak bardziej konkretnie to tak:

  1. Raczej MVP a nie klasyczny MVC
    http://stackoverflow.com/questions/4530023/does-php-supports-mvp-pattern

Jest dość istotna różnica między klasycznym podręcznikowym podejściem, bo w tym przypadku chodzi o coś co się nazywa Passive View, wyjaśnione pod linkiem wyżej.

  1. Będzie więcej tych wzorców
  • Layout Pattern
  • Factory
  • Data Mapper / Active Record
  • Singleton
  1. Oczywiście dojdzie wiele innych rzeczy takich jak routing, w sensie każda podstrona to odpowiednie użycie kontroler-akcja, Query Builder, config, cache, systemy szablonów itd

  2. To wszystko implementują frameworki, pisać to od zera to bardzo dużo pracy.

0

Tak jak @pugn pisał wzorzec projektowy to tak jak nazwa wskazuje wzór według którego powinno tworzyć się programy. Nic nie wklejasz do kodu, tylko tworzysz kod aby spełniał kryteria. Jednocześnie nie idź w 100% w MVC bo są projekty w których będzie on bardziej przeszkadzał niż pomagał. Najlepiej gdybyś poznał każdy z nich (tych wzorców) i pomyślał do czego może każdy się przydać.
Co do sklepu internetowego to musisz napisać o co chodzi z tą "dużą ilością podstron".
Zasadniczo są 4 widoki.
-lista produktów
-widok produktu
-widok płatności
-strony "tekstowe" (np. regulamin)

I akurat w aplikacji o której piszesz MVC będzie chyba najlepszym wyborem ponieważ potrzebujesz tylko 4 kontrolerów.
Jeden od wyświetlania listy produktów - na głównej pokazują się jakieś defaultowe. Ktoś wpisze coś w wyszukiwarkę, nadal używasz tego jednego kontrolera lecz teraz odwołujesz się do innej metody (metoda to taka funkcja lecz w klasie) modelu. A wszystko wrzucasz w to samo view zmieniając tylko nagłówek. Czyli w kontrolerze odwołujesz się ciągle do tego samego pliku widoku podając tylko $header w którym jest tekst jaki ma się pojawiać nad produktami i tablice z samymi produktami.
Jeden do wyświetlania konkretnego produktu - cały czas wszystko działa tak samo z tym że pobierasz informacje o innym produkcie.
Jeden do płatności - chyba nie wymaga komentarza.
Jeden do stron na których jest sam tekst - po prostu biorąc pod uwagę parametry pobierasz inny plik widoku czy inne dane z bazy, ale cały czas wszystko działa tak samo.

Oczywiście można to rozbudowywać o kolejne kontrolery do np. newsletter'a.
Dzięki takiemu podejściu możesz zamknąć aplikację w parunastu plikach, i przy zmianie zmianie czegoś (np. zmiana bazy) zmieniasz kod tylko w modelach zamiast szukać czy gdzieś jeszcze nie jest odwołanie do bazy.

0

Od kilku dni uczę się obiektówki i już co nieco ogarniam, lecz zdaję sobie sprawę że to za mało. Mniej więcej wiem na czym polega ten model projektowania. Lecz nie mogę pojąć w jaki sposób te katalogi się ze sobą kontaktują.
Czy jeśli stworzyłbym swój skrypt, który w jednym folderze.php miałby metody i poprzez tworzenie nowego obiektu klasy(z foldera.php) i odwoływanie się do poszczególnych metod i zmiennych wyświetlał by całość na ekranie i pobierał dane z bazy. Można to uznać za model?

0

Chyba Cię nie zrozumiałem. To o czym piszesz to zwykłe programowanie strukturalne tylko zamiast funkcji masz metody. Ogólnie ma to działać tak:
Core ładuje odpowiedni kontroler. Z poziomu tego kontrolera tworzysz instancje modelu. Następnie dane (czyli w tym wypadku lista produktów) przesyłasz do widoku i to on jako jedyny zwraca coś do przeglądarki. Oczywiście przydała by się klasa loader'a która załaduje model i jakiś "parser" który wyświetli widok.

0

Bardziej chodzi mi o odwoływanie się do tych metod i właściwości poprzez $this i tworzenie nowych obiektów w folderze, który wyświetla dane.
Przykład
Tę metodę mam w klasie USER w folderze gdzie znajdują się wszystkie metody.

public function register($nick,$haslo,$rehaslo,$email,$data)
	{
		 
			$stmt= $this->db->prepare("INSERT INTO `uzytkownicy` (nick, email, haslo, data) VALUES(:nick,:email,:haslo,:data)") or die("Nie  mogłem Cie zarejestrować!");
			$stmt->bindParam(":nick", $nick, PDO::PARAM_STR);
			$stmt->bindParam(":email", $email, PDO::PARAM_STR);
			$stmt->bindParam(":haslo", $haslo, PDO::PARAM_STR);
			$stmt->bindParam(":data", $data, PDO::PARAM_STR);
			$stmt->execute();
			return $stmt;
	} 

Tutaj mam skrypt wyświetlający formularz i sprawdzający dane użytkownika, znajdujący się w folderze wyświetlającym.

<?php
include_once 'dbconfig.php';

if($user->is_loggedin()!="")
{
    $user->redirect('home.php');
}

if(isset($_POST['wyslano']))
{
    $nick = trim($_POST['nick']);
    $haslo	= trim($_POST['haslo']);
	$rehaslo = trim($_POST['rehaslo']);
    $email = trim($_POST['email']);
	$data = date("Y-m-d");
	
	if(strlen($nick)<3)
	{
		$error[]= 'Login musi zawierać co najmniej 3 znaki<br>';
	}
	else if(strlen($haslo)<6)
	{
		$error[]= 'Hasło musi zawierać co najmniej 6 znaków<br>';
	}
	else if(strlen($haslo) != strlen($rehaslo))
	{
		$error[]= 'Hasła nie pasują do siebie<br>';
	}
	else if(!preg_match('/\@/', $email) || strlen($email)<5)
	{
		$error[]= 'Podany adres e-mail jest nieprawidłowy<br>';
	}
   else
   {
      try
      {
         $stmt = $DB_con->prepare("SELECT nick,email FROM uzytkownicy WHERE nick=:nick OR email=:email");
         $stmt->execute(array(':nick'=>$nick, ':email'=>$email));
         $row=$stmt->fetch(PDO::FETCH_ASSOC);
    
         if($row['nick']==$nick) {
            $error[] = "zły nick! !";
         }
         else if($row['email']==$email) {
            $error[] = "Zły mail!";
         }
         else
         {
            if($user->register($nick,$haslo,$rehaslo,$email,$data))
            {
                $user->redirect('facebook.com');
            }
         }
     }
     catch(PDOException $e)
     {
        echo $e->getMessage();
     }
  } 
}

?>


<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Sign up : cleartuts</title>
<link rel="stylesheet" href="cos.css" type="text/css"  />
</head>
<body>
        <form action="" method="post">
            <h2>Sign up.</h2><hr />
            <?php
            if(isset($error))
            {
               foreach($error as $error)
               {
                  ?>
                  <div class="alert alert-danger">
                       <?php echo $error; ?>
                  </div>
                  <?php
               }
            }

            ?>

            <label for="nick">Login</label>
            <input type="text" name="nick" required>
            <br>
            <label for="password">Hasło</label>
            <input type="password" name="haslo" required>
            <br>
			<label for="rehaslo">Powtórz Hasło</label>
			<input type="password" name="rehaslo" required>
            <label for="email">E-mail</label>
            <input type="text" name="email" required>
            <br>
            <input type="submit" name="wyslano" value="Załóż konto"/>
        </form>
       </div>
</div>

</body>
</html>
 

W folderze dbconfig.php znajduje się tworzenie nowego obiektu klasy USER ($user - new USER) i łączenie się z bazą. W clasie znajduje się konstruktor, łączący z bazą.

Może być taki sposób?

2

Wydaje mi się że nie rozumiesz na czym to polega. A więc po kolei. Załóżmy że masz prosty projekt w jednym folderze i tam masz wszystkie pliki PHP. I chcesz wyciągnąć dane użytkownika z bazy o przykładowym id = 1.

Model.php

<?php

class Model
{
    private $data = array();
   
    public function find($id)
    {
        $result = DB::select('SELECT * FROM users WHERE id = :id', array(':id' => $id));
       
        if ($result)
        {
            $model = new Model;
            $model->id = $result->id;
            $model->username = $result->username;
            $model->email = $result->email;
            return $model;
        }
        return NULL;
    }
    
    public function __get($key)
    {
        return isset($this->data[$key]) ? $this->data[$key] : NULL;
    }
   
    public function __set($key, $value)
    {
        $this->data[$key] = $value;
    }    
}

View.php (ta klasa będzie użyta do renderowania wszystkich templatek i będzie tylko jedna)

<?php

class View
{
   public function render($path, array $vars = array())
   {
      ob_start();
      extract($vars);
      require($path);
      return ob_get_clean();
   }
}

template.php (to będzie odpowiedzialne za wyświetlanie danych, tutaj także będą formularze)

<!DOCTYPE html>
<html>
<head>
  <title>User</title>
</head>

<body>
      <p>Id: <?= $user->id ?></p>
      <p>Username: <?= $user->username ?></p>
      <p>E-mail: <?= $user->email ?></p>
</body>
</html>

Controller.php (steruje przepływem informacji):

<?php

class Controller
{
     private $model;
     private $view;

     public function __construct(Model $model, View $view)
     {
         $this->model = $model;
         $this->view = $view;
     }

     public function indexAction()
     {
        // pobieranie danych z modelu
        $id = 1;
        $user = $this->model->find($id);        
        // i przekazywanie ich do widoku
        return $this->view->render('template.php', array('user' => $user));
     }
}

DB.php (klasa helper, odpowiada za połączenia z bazą danych):

<?php

class DB
{    
    public static $driver = 'mysql';
    public static $hostname = 'localhost';
    public static $database = 'mojabaza';
    public static $username = 'root';
    public static $password = 'root';
    
    public static function select($sql, array $params)
    {        
        $dsn = DB::$driver . ':host=' . DB::$hostname
            . ';dbname=' . DB::$database;                
        $db = new PDO($dsn, DB::$username, DB::$password);
        
        $stmt = $db->prepare($sql);
        $stmt->execute($params);        
        return $stmt->fetch(PDO::FETCH_OBJ);
    }
}

index.php:

<?php
// automatyczne ładowanie klas
spl_autoload_extensions(".php");
spl_autoload_register();

$model   = new Model();
$view    = new View();

$controller = new Controller($model, $view);
echo $controller->indexAction();

I nie wiem czy załapiesz o co chodzi. Jest podzielone i ściśle zdefiniowane co i za co odpowiada, w tym przypadku masz tu coś na kształt Active Record w przypadku modelu Model.php, to reprezentuje dane które masz w tabeli users w bazie. Widok jest pasywny i jest generowany z szablonu template.php za pomocą klasy View a kontroler wszystkim steruje.

Oczywiście można by to zrobić na wiele innych sposobów.

0

Bardzo Mi rozjaśniłeś sytuację. Jednak nie rozumiem metody render w clasie view, może dla tego że nie znam funkcji, które w niej zawarłeś, trzeba się nauczyć. Dziękuje za szczegółowe opisanie sytuacji.

0

ob_start rozpoczyna buffer danych. Tj. póki nie użyjesz funkcji ob_end_flush() nic nie powinno Ci się wyświetlić.
Funkcja extract zamienia Ci tablicę na zmienne tj:

$array = (
    'var' => 'data',
    'rav' => 'atad',
);

Da taki sam efekt jak:

$var = 'data';
$rav = 'atad';

Dodatkowo nie trzymałbym wszystkiego w jednym folderze. Po prostu dla porządku a w pliku który odpala jakiegoś bootstrapa czy ładuje inne klasy zdefiniować foldery:

define('VIEW_FOLDER', '/view/');

Dzięki temu nie będziesz musiał szukać pliku w grupie 50 innych tylko paru. Jak nagle zapragniesz zmienić nazwę folderu to wystarczy tylko zmienić definicję. No i oczywiście używaj potem zdefiniowanych kluczy.

0

Idziemy dalej. Załóżmy że:

  • kontrolery będą w podkatalogu classes/Controller
  • modele będą w podkatalogu classes/Model
  • widoki (templatki) będą w podkatalogu views

index.php będzie nadal w głównym katalogu projektu.

Po kolei:

classes/Model/User.php

<?php

class Model_User
{
    private $data = array();
   
    public function find($id)
    {
        $result = DB::select('SELECT * FROM users WHERE id = :id', array(':id' => $id));
       
        if ($result)
        {
            //tu było new Model a teraz musi byc Model_User
            $model = new Model_User;
            $model->id = $result->id;
            $model->username = $result->username;
            $model->email = $result->email;
            return $model;
        }
        return NULL;
    }
   
    public function __get($key)
    {
        return isset($this->data[$key]) ? $this->data[$key] : NULL;
    }
   
    public function __set($key, $value)
    {
        $this->data[$key] = $value;
    }    
}

classes/Controller/User.php

<?php

class Controller_User
{
     private $model;

     private $view;

     // Było Model $model a teraz Model_User $model
     public function __construct(Model_User $model, View $view)
     {
         $this->model = $model;
         $this->view = $view;
     }

     public function indexAction()
     {
        $id = 1;
        $user = $this->model->find($id);        
        // właściwie to można by zrezygnować z tego .php dając tylko samą nazwę szablonu
        // a to rozszerzenie .php dawać w View::render
        return $this->view->render('template.php', array('user' => $user));
     }
}

classes/View.php

<?php

class View
{
   public function render($path, array $vars = array())
   {
      ob_start();
      extract($vars);
      require(DOCROOT . 'views' . DIRECTORY_SEPARATOR . $path);
      return ob_get_clean();
   }
}

plik classes/DB.php będzie niezmieniony ale tam musi być, pomijam go tutaj, tak samo jak views/template.php, który też będzie bez zmian.

i teraz index.php w głównym katalogu projektu:

<?php

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

//to na bazie tego będzie autoload działający w oparciu o położenie plików
//oraz ich nazewnictwo (liczą się duże i małe litery w nazwach plików i katalogów)

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

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

    return FALSE;
}

spl_autoload_register('autoload');

$model   = new Model_User();
$view    = new View();

$controller = new Controller_User($model, $view);
echo $controller->indexAction();

Natomiast nie wiem jak wygląda sprawa z użyciem (na końcu) spl_autoload_unregister. W każdym razie do własnego frameworka jeszcze droga daleka :-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1