Jak załadować klasy oparte na ładnych adresach URL na stronie podobnej do MVC?

Chciałbym poprosić o kilka wskazówek, jak rozwiązać ten problem. Próbuję zbudować własną stronę MVC. Nauczyłem się podstaw adresu URL.

http://example.com/blog/cosplay/cosplayer-expo-today

Blog - > kontroler
cosplay - > metoda w kontrolerze
cosplay - > zmienna w metodzie

Co zrobić, jeśli dynamicznie rozszerzę kategorię w moim kontrolerze bloga? Czy będę musiał stworzyć metodę, czy jest jakaś sztuczka, aby zrobić to automatycznie? To znaczy... mam teraz te kategorie: cosplay, gra, film,serial. Muszę więc utworzyć te metody w kontrolerze, ale wszystkie robią to samo, czyli wybierają inną kategorię z bazy danych.

  • function cosplay() = example.com/blog/cosplay /
  • function game() = example.com/blog/game /
  • function movie() = example.com/blog/movie /
  • function series() = example.com/blog/series /

Czy jest jakaś dobra rada, jak mogę napisać kontroler, aby zrobił to automatycznie? To znaczy, jeśli prześlij nową kategorię do mojej bazy danych, ale nie chcę modyfikować kontrolera. Czy to możliwe? Dzięki za pomoc!

UPDATE

Oto moja klasa Eksploratora URL

class Autoload
{
    var $url;
    var $controller;
    function __construct()
    {
        $this->url = $_GET['url'];
        //HNEM ÜRES AZ URL
        if($this->url!='' && !empty($this->url))
        {
            require 'application/config/routes.php';
            //URL VIZSGÁLATA
            $this->rewrite_url($this->url);

            //URL SZÉTBONTÁSA
            $this->url = explode('/', $this->url);

            $file = 'application/controllers/'.$this->url[0].'.php';
            //LÉTEZIK A CONTROLLER?
            if(file_exists($file))
            {
                require $file;
                $this->controller = new $this->url[0];

                //KÉRELEM ALATT VAN AZ ALOLDAL?
                if(isset($this->url[1]))
                {
                    //LÉTEZIK A METÓDUS? ENGEDÉLYEZVE VAN?
                    if(method_exists($this->controller, $this->url[1]) && in_array($this->url[1], $route[$this->url[0]]))
                    {
                        if(isset($this->url[2]))
                        {
                            $this->controller->{$this->url[1]}($this->url[2]);
                        }
                        else
                        {
                            $this->controller->{$this->url[1]}();
                        }
                    }
                    else
                    {
                        header('location:'.SITE.$this->url[0]);
                        die();
                    }
                }
            }
            else
            {
                header('location:'.SITE);
                die();
            }
        }
        else
        {
            header('location:'.SITE.'blog');
            die();
        }
    }

    /**
     * Első lépésben megvizsgáljuk, hogy a kapott szöveg tartalmaz-e nagybetűt. Amennyiben igen átalakítjuk kisbetűsre.<br/>
     * Második lépésben megnézzük, hogy a kapott szöveg '/'-re végződik-e. Amennyiben igen levágjuk azt.<br/>
     * Harmadik lépésben újra töltjük az oldalt a formázott szöveggel.
     * 
     * @param string $url Korábban beolvasott URL.
     */
    private function rewrite_url($url)
    {
        //HA NAGYBETŰ VAN AZ URL-BEN VAGY '/'-RE VÉGZŐDIK
        if(preg_match('/[A-Z]/', $url) || substr($url, -1)=='/')
        {
            //NAGYBETŰS AZ URL KICSIRE ALAKÍTJUK
            if(preg_match('/[A-Z]/', $url))
            {
                $url = strtolower($url);
            }
            //HA '/'-RE VÉGZŐDIK LEVÁGJUK
            if(substr($url, -1)=='/')
            {
                $url = substr($url, 0, strlen($url)-1);
            }
            header('location:'.SITE.$url);
            die();
        }
    }




}
A oto mój .htacces
Options +FollowSymLinks
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d  
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l

RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
Author: tereško, 2013-09-10

2 answers

FYI: jest kilka rzeczy, które robisz źle. Postaram się przejść przez każdy z nich i wyjaśnić problemy, nieporozumienia i możliwe rozwiązanie(y).

Automatyczne ładowanie i routing to oddzielne rzeczy.

Z wyglądu Twojego kodu wynika, że masz jedną klasę, która odpowiada za następujące zadania:]}

  • routing: dzieli adres URL na części, które mają pewne znaczenie dla pozostałych aplikacji
  • autoloading: Klasa pobiera oddzielone segmenty URL i próbuje dołączyć powiązany kod
  • factory: inicjowane są nowe instancje i wywoływane są na nich niektóre metody
  • W niektórych przypadkach Klasa wysyła odpowiedź do użytkownika w postaci nagłówka HTTP

W OOP jest coś, co nazywa się: zasada pojedynczej odpowiedzialności [wersja skrócona]. Zasadniczo oznacza to, że klasa powinna być Obsługa jednego konkretnego są rzeczy. Powyższa lista stanowi co najmniej 4 różne obowiązki dla klasy Autoload.

Zamiast tego, co masz teraz, każde z tych ogólnych zadań powinno być obsługiwane przez oddzielną klasę. A w przypadku automatycznego ładowania, można uciec z jednej funkcji.

Jak napisać własny kod automatycznego ładowania ?

Część problemu, który widzę jest zamieszanie o tym, jak autoload faktycznie działa w PHP. Wywołanie include lub require nie musi być wykonywane gdzie instancja zostanie utworzona. Zamiast tego rejestrujesz handler (używając spl_autoload_register() funkcja), która następnie jest* * automatycznie * wywoływana podczas próby użycia wcześniej niezdefiniowanej klasy.

Najprostszym przykładem jest:

spl_autoload_register( function( $name ) use ( $path ) {
    $filename = $path . '/' . $name . '.php';
    if ( file_exists( $filename ) === true ) {
        require $filename;
        return true;
    }
    return false;
});

Ten konkretny przykład wykorzystuje funkcję anonymous, która jest jedną z funkcji wprowadzonych w PHP 5.3, ale strona podręcznika spl_autoload_register() pokaże Ci również przykłady, jak osiągnąć to samo z obiektów lub zwykłych funkcji.

Kolejną nową funkcją, która jest ściśle związana z automatycznym ładowaniem, jest przestrzenie nazw . W tym kontekście przestrzenie nazw dałyby dwie natychmiastowe korzyści: możliwość posiadania wielu klas o tej samej nazwie i opcji ładowania pliku klasy z wielu katalogów.

Na przykład, możesz mieć taki kod:

$controller = new \Controllers\Overview;
$view = new \Views\Overview;

$controller->doSomething( $request );

.. w tym przypadku możesz mieć Autoloader pobierający klasy odpowiednio z plików /project/controllers/overview.php i /project/views/overview.php. Ponieważ spl_autoload_register() przekaże "\Controllers\Overview" i "\Views\Overview" do funkcji obsługi.

Istnieje również rys. zalecenie, jak zaimplementować automatyczne ładowanie. Możesz go znaleźć tutaj . Chociaż ma pewne znaczące problemy, powinien zapewnić Ci dobrą podstawę, na której możesz się oprzeć.

Jak analizować ładne adresy URL ?

Nie jest tajemnicą, że Apache mod_rewrite jest dość ograniczony w tym, co może zrobić z pretty Url. I choć jest to powszechne serwer, nie jest to jedyna opcja dla webserverów. Dlatego dla maksymalnej elastyczności Programiści PHP decydują się na obsługę adresów URL na końcu PHP.

I pierwszą rzeczą jaką zrobi każdy nowicjusz jest explode('/', ... ). Jest to naturalny wybór, ale wkrótce zauważysz, że jest on również ekstremalnie Ograniczony W tym, co naprawdę może zrobić. Mechanizm routingu zacznie się rozwijać. Początkowo na podstawie liczby segmentów, później-dodawanie różnych wartości warunkowych w segmentach, które wymagają różnych zachowanie.

Zasadniczo, to zamieni się w ogromny, kruchy i niekontrolowany bałagan. Zły pomysł.

Zamiast tego powinieneś mieć listę wyrażeń regularnych, które pasują do podanego adresu URL. Na przykład:

'#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#'

Powyższy wzór pasuje do wszystkich adresów URL, które mają dwa segmenty, z tekstem w pierwszym segmencie i "foobar" w drugim ... jak "/testme/foobar".

Dodatkowo można powiązać każdy wzór z odpowiednimi wartościami domyślnymi dla każdego mecz. Po złożeniu tego wszystkiego, możesz skończyć z taką konfiguracją (używa składni 5.4 + array, ponieważ tak lubię pisać .. "deal with it"): {]}

$routes = [
    'primary' => [
        'pattern'   => '#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#',
        'default'   => [
            'action'    => 'standard',
        ],
    ],
    'secundary' => [
        'pattern'   => '#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#',
        'default'   => [
            'resource'  => 'catalog',
            'action'    => 'view',
        ]
    ],
    'fallback'  => [
        'pattern'   => '#^.*$#',
        'default'   => [
            'resource'  => 'main',
            'action'    => 'landing',
        ],
    ],
]; 

Które można obsługiwać za pomocą następującego kodu:

// CHANGE THIS
$url = '/12345/product';

$current = null;

// matching the route
foreach ($routes as $name => $route) {
    $matches = [];
    if ( preg_match( $route['pattern'], $url, $matches ) ) {
        $current = $name;
        $matches = $matches + $route['default'];
        break;
    }
}


// cleaning up results
foreach ( array_keys($matches) as $key ) {
    if ( is_numeric($key) ) {
        unset( $matches[$key] );
    }
}


// view results
var_dump( $current, $matches );

Live code: tutaj lub tutaj

Uwaga:
W przypadku stosowania '(?P<name> .... )' notacji, dopasowania zwrócą tablicę z 'name' jako klucz. Przydatna sztuczka dla więcej niż routing.

Prawdopodobnie będziesz chciał wygenerować wyrażenia regularne dla dopasowania z bardziej czytelnych notacji. Na przykład w pliku konfiguracyjnym wyrażenie:

'#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#'

.. powinno wyglądać tak:

'/:id[[/:resource]/:action]'

Gdzie :param oznaczałoby segment URL, a [...] oznaczałoby opcjonalną część URL.

Na tej podstawie powinieneś być w stanie stworzyć swój własny system routingu. Powyższe fragmenty kodu to tylko przykład uproszczonej podstawowej funkcjonalności. Aby uzyskać pewną perspektywę na to, jak może wyglądać, gdy jest w pełni zaimplementowany, możesz spojrzeć na kod w tej odpowiedzi. Powinno dać ci kilka pomysłów na własną wersję API.

Wywoływanie rzeczy na kontrolerach ..

Dość powszechnym błędem jest zakopywanie wykonania kontrolerów gdzieś głęboko w klasie routingu (lub klasach).Powoduje to dwa problemy:

  • zamieszanie: trudniej jest znaleźć, gdzie " prawdziwy praca " rozpoczyna się w aplikacji
  • Połączenie: twój router kończy się przykuty łańcuchem do specyficznej interpretacji architektury podobnej do MVC]}

Routing jest zadaniem, które nawet w niestandardowej aplikacji będzie naturalnie przyciągać do "Framework-owskiej" części kodu.

(naprawdę) uproszczone wersje wyglądałyby następująco:

$matches = $router->parse( $url );

$controller = new {'\\Controller\\'.$matches['controller']};
$controller->{$matches['action']( $matches );

W ten sposób nie ma nic, co wymagałoby, aby wyniki routingu były używane w architekturze podobnej do MVC. Może po prostu potrzebujesz gloryfikowany mechanizm pobierania służący do serwowania statycznych plików HTML.

A co z tymi dynamicznie rozszerzającymi się kategoriami?

Źle na to patrzysz. nie ma potrzeby dynamicznego dodawania metod do kontrolera. w twoim przykładzie jest właściwie jedna metoda kontrolera ... coś w rodzaju:
public function getCategory( $request ) {
    $category = $request->getParameter('category');

    // ... rest of your controller method's code
}

Gdzie $category skończyłoby się zawierające "cosplay", "game", "movie", "series" lub jakiejkolwiek innej kategorii, którą dodałeś. To jest coś, co twój kontroler przejdzie do warstwy modelu, aby odfiltrować artykuły.

Jakich ludzi używa się zawodowo?

Te dni, ponieważ wszyscy (dobrze .. każdy ma jakąś wskazówkę) używa composer , do automatycznego ładowania najlepszą opcją jest użycie loadera dołączonego do composera.

Wystarczy dodać require __DIR__ . '/vendor/autoload.php' i z jakąś konfiguracją to po prostu zadziała.

Jeśli chodzi o routing, istnieją dwa główne "samodzielne" rozwiązania: FastRoute lub komponent routingu Symfony . Te mogą być zawarte w projekcie bez dodatkowych bólów głowy.

Ale ponieważ niektórzy ludzie będą używać frameworków, każdy z nich będzie również zawierał możliwość routingu żądań.

Kilka dalszych lektur ..

Jeśli chcesz dowiedzieć się więcej o wzorze architektonicznym MVC, zdecydowanie polecam Ci przejść do wszystkich materiałów wymienionych w this post. Potraktuj to jako obowiązkowe lista czytania/oglądania. Możesz również znaleźć nieco korzystne te stare posty z moich na tematy związane z MVC: tutaj, tutaj i tutaj

P. S.: ponieważ PHP 5.0 zostało wydane (jakiś czas w 2004 roku), zmienne klasy powinny być zdefiniowane za pomocą public, private lub protected zamiast var.

 35
Author: tereško,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-22 20:00:15

Myślę, że za dużo o tym myślisz....

Więc kontrolerem / zasobem jest blog....metoda, z której wszystkie powinny być uruchamiane to "read" (za pomocą crud...Zwykle nazywam to danymi, ale w zasadzie to twój wybór). Teraz wystarczy, że twoja metoda zaakceptuje wartość kategorii, która automatycznie zostanie zmapowana na podstawie adresu url....

Oto próbka...całkowicie nieprzetestowane, ale tylko po to, aby pokazać ci pomysł (używając pdo)
public function data($id = 0, $category = 0){
    if (isset($id) AND $id != 0){
         $bind = array(":id", $id);
         $results = $db->query("SELECT * FROM blog WHERE blog_id = :id", $bind);
         return $results[0];
    } else if (isset($category) AND $id != 0){ 
         $approved_categories = array("cosplay","game","movie","series");
         if (in_array($category, $approved_categories)){
              $bind = array(":cat", $category);
              $results = $db->query("SELECT * FROM blog WHERE blog_cat = :cat", $bind);
         } 
         return $results;
    }
}
 -3
Author: hendr1x,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-10 19:33:35