Intereting Posts

Как загружать классы на основе симпатичных URL-адресов на странице MVC?

Я хотел бы попросить несколько советов, как решить эту проблему. Я пытаюсь создать собственный веб-сайт MVC. Я узнал основы URL.

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

блог -> контроллер
cosplay -> метод в контроллере
cosplayer-expo-today -> переменная в методе

Что делать, если я динамически расширяю категорию в своем блочном контроллере? Мне нужно создать метод, или есть какой-то трюк, чтобы сделать это автоматически? Я имею в виду … у меня есть эти категории сейчас: косплей, игра, фильм, серия. Поэтому мне нужно создать эти методы в контроллере, но все они делают то же самое, а именно выбирают другую категорию из базы данных.

  • функция cosplay () = example.com/blog/cosplay/
  • function game () = example.com/blog/game/
  • function movie () = example.com/blog/movie/
  • function series () = example.com/blog/series/

Есть ли хорошие советы о том, как я могу написать свой контроллер, чтобы сделать это автоматически? Я имею в виду, если я загружу новую категорию в свою базу данных, но я не хочу изменять контроллер. Является ли это возможным? Спасибо за помощь!

ОБНОВИТЬ

Вот мой класс класса 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('/[AZ]/', $url) || substr($url, -1)=='/') { //NAGYBETŰS AZ URL KICSIRE ALAKÍTJUK if(preg_match('/[AZ]/', $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(); } } } проведения 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('/[AZ]/', $url) || substr($url, -1)=='/') { //NAGYBETŰS AZ URL KICSIRE ALAKÍTJUK if(preg_match('/[AZ]/', $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(); } } } проведения 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('/[AZ]/', $url) || substr($url, -1)=='/') { //NAGYBETŰS AZ URL KICSIRE ALAKÍTJUK if(preg_match('/[AZ]/', $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(); } } } 

И вот мой .htacces

 Options +FollowSymLinks RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] 

FYI: Есть несколько вещей, которые вы делаете неправильно. Я попытаюсь пройти через каждый из них и объяснить проблемы, неправильные представления и возможные решения.

Автозагрузка и маршрутизация – это отдельные вещи.

Из внешнего вида вы опубликовали код, очевидно, что у вас есть один класс, который отвечает за следующие задачи:

  • маршрутизация: он разбивает URL-адрес на части, которые имеют некоторое значение для остальных приложений
  • автозагрузка: класс принимает разделенные сегменты URL и пытается включить связанный код
  • factory: новые экземпляры инициализируются, и на них вызывается некоторый метод
  • ответ: в некоторых случаях класс отправляет ответ пользователю в виде HTTP-заголовка

В ООП есть эта вещь, называемая: принцип единой ответственности [ короткая версия ] . В основном это означает, что класс должен обрабатывать одну конкретную вещь. Вышеуказанный список составляет не менее 4 различных обязанностей для вашего класса Autoload .

Вместо того, что у вас есть сейчас, каждая из этих общих задач должна обрабатываться отдельным классом. А в случае автозагрузчика вы можете уйти с одной функцией.

Как написать собственный код автозагрузки?

Часть проблемы, которую я вижу, представляет собой путаницу в том, как autoload действительно работает в PHP. Вызов include или require не должен выполняться там, где будет создан экземпляр. Вместо этого вы регистрируете обработчик (используя spl_autoload_register() ), которая затем ** автоматически * вызывается, когда вы пытаетесь использовать ранее не определенный класс.

Самый простой пример:

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

В этом конкретном примере используется анонимная функция , которая является одной из функций, которая была введена в PHP 5.3, но на странице руководства для spl_autoload_register() также будут показаны примеры того, как достичь того же с объектами или обычными функциями.

Еще одна новая функция, тесно связанная с автозагрузкой, – это пространства имен . В этом контексте пространства имен предоставят вам две непосредственные преимущества: возможность иметь несколько классов с одинаковым именем и опциями для загрузки файла класса из нескольких каталогов.

Например, у вас может быть такой код:

 $controller = new \Controllers\Overview; $view = new \Views\Overview; $controller->doSomething( $request ); 

.. в этом случае вы можете получить классы /project/controllers/overview.php автозагрузчика из /project/controllers/overview.php и /project/views/overview.php файлов соответственно. Поскольку spl_autoload_register() передаст функции "\Controllers\Overview" и "\Views\Overview" функции обработчика.

Существует также рекомендация FIG о том, как реализовать автозагрузчики. Вы можете найти его здесь . Хотя у него есть некоторые серьезные проблемы, он должен предоставить вам хорошую базу, на которой можно опираться.

Как разбирать красивые URL-адреса?

Не секрет, что mod_rewrite от Apache довольно ограничен в том, что он может делать с симпатичными URL-адресами . И, хотя это широко распространенный сервер, это не единственный вариант для веб-серверов. Вот почему для максимальной гибкости разработчики PHP предпочитают обрабатывать URL-адреса в конце PHP.

И первое, что сделает любой новичок, – это explode('/', ... ) . Это естественный выбор, но вы скоро заметите, что он также чрезвычайно ограничен в том, что он действительно может сделать. Механизм маршрутизации начнет расти. Сначала на основе подсчета сегментов, позже – добавление различных условных значений в сегменты, которые требуют различного поведения.

По сути, это превратится в огромный, хрупкий и неуправляемый беспорядок. Плохая идея.

Вместо этого вы должны иметь список регулярных выражений, которые вы сопоставляете с заданным довольно URL. Например:

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

Вышеупомянутый шаблон будет соответствовать всем URL, которые имеют два сегмента, с некоторым текстом в первом сегменте и "foobar" во втором … например "/testme/foobar" .

Кроме того, вы можете связать каждый шаблон с соответствующими значениями по умолчанию для каждого совпадения. Когда вы соедините все это вместе, вы можете получить такую ​​конфигурацию (используйте синтаксис 5,4+, потому что я так хочу писать .. с этим справимся):

 $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', ], ], ]; 

Который вы могли бы использовать, используя следующий код:

 // 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 ); 

Живой код: здесь или здесь

Заметка:
Если вы используете нотацию '(?P<name> .... )' , совпадения возвращают массив с 'name' в качестве ключа. Полезный трюк для более маршрутизации.

Вероятно, вам захочется генерировать регулярные выражения для соответствия с некоторыми более читаемыми обозначениями. Например, в файле конфигурации это выражение:

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

.. вероятно, должен выглядеть примерно так

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

Если параметр :param указывает на сегмент URL, а [...] будет указывать необязательную часть URL-адреса.

Исходя из этого, вы должны иметь возможность настроить свою собственную систему маршрутизации. Фрагменты кода, приведенные выше, являются просто примером упрощенной функциональности ядра. Чтобы получить представление о том, как он может выглядеть при полной реализации, вы можете посмотреть код в этом ответе . Он должен дать вам несколько идей для вашей собственной версии API.

Вызов материала на контроллерах ..

Это довольно распространенная ошибка, чтобы похоронить выполнение контроллеров где-то глубоко в классе маршрутизации (или классах). Это вызывает две проблемы:

  • путаница: сложнее найти, где начинается «настоящая работа» в приложении
  • соединение: ваш маршрутизатор попадает в цепочку с определенной интерпретацией MVC-подобной архитектуры

Маршрутизация – это задача, которая даже в специально написанном приложении, естественно, будет тяготеть к «каркасной» части кода.

(Действительно) упрощенные версии выглядели бы так:

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

Таким образом, нет ничего, что требовало бы, чтобы ваши результаты маршрутизации использовались в некоторой MVC-подобной архитектуре. Возможно, вам просто нужен прославленный механизм для загрузки статических файлов HTML.

Как насчет тех, кто динамически расширяет категории?

Вы смотрите на это неправильно. Нет необходимости динамически добавлять методы к контроллеру. В вашем примере есть фактически один метод контроллера … что-то вроде:

 public function getCategory( $request ) { $category = $request->getParameter('category'); // ... rest of your controller method's code } 

Где $category будет содержать "cosplay" , "game" , "movie" , "series" или любую другую категорию, которую вы добавили. Это то, что ваш контроллер перейдет к слою модели, чтобы отфильтровать статьи.

Что люди на самом деле используют профессионально?

В эти дни, поскольку все (ну .. все с некоторой подсказкой) используют композитор , для автозагрузки лучше всего использовать загрузчик, который поставляется в комплекте с композитором.

Вы просто добавляете require __DIR__ . '/vendor/autoload.php' require __DIR__ . '/vendor/autoload.php' и с некоторой конфигурацией он просто будет работать.

Что касается маршрутизации, существуют два основных «автономных» решения: FastRoute или компонент маршрутизации Symfony . Эти могут быть включены в ваш проект без дополнительных головных болей.

Но так как некоторые люди будут использовать фреймворки, каждый из них также будет содержать возможность маршрутизации запросов.

Дальнейшее чтение ..

Если вы хотите узнать больше о архитектурном шаблоне MVC, я настоятельно рекомендую вам пройти все материалы, перечисленные в этом сообщении . Думайте об этом как обязательном чтении / просмотре списка. Вы также можете найти несколько полезных данных моих старых должностей по темам, связанным с MVC: здесь , здесь и здесь

PS: поскольку был выпущен PHP 5.0 (некоторое время в 2004-м), переменные класса должны быть определены с использованием public , private или protected вместо var .

Я думаю, ты уже думаешь об этом …

Таким образом, контроллер / ресурс – это блог …. метод, из которого все они должны запускаться, – это «читать» (используя crud … я обычно называю это данными, но в основном ваш выбор). Теперь просто попросите свой метод принять значение категории, которое автоматически будет отображаться на основе URL.

вот образец … полностью непроверенный, но просто чтобы показать вам идею (используя 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; } }