Я хотел бы попросить несколько советов, как решить эту проблему. Я пытаюсь создать собственный веб-сайт MVC. Я узнал основы URL.
http://example.com/blog/cosplay/cosplayer-expo-today
блог -> контроллер
cosplay -> метод в контроллере
cosplayer-expo-today -> переменная в методе
Что делать, если я динамически расширяю категорию в своем блочном контроллере? Мне нужно создать метод, или есть какой-то трюк, чтобы сделать это автоматически? Я имею в виду … у меня есть эти категории сейчас: косплей, игра, фильм, серия. Поэтому мне нужно создать эти методы в контроллере, но все они делают то же самое, а именно выбирают другую категорию из базы данных.
Есть ли хорошие советы о том, как я могу написать свой контроллер, чтобы сделать это автоматически? Я имею в виду, если я загружу новую категорию в свою базу данных, но я не хочу изменять контроллер. Является ли это возможным? Спасибо за помощь!
ОБНОВИТЬ
Вот мой класс класса 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: Есть несколько вещей, которые вы делаете неправильно. Я попытаюсь пройти через каждый из них и объяснить проблемы, неправильные представления и возможные решения.
Из внешнего вида вы опубликовали код, очевидно, что у вас есть один класс, который отвечает за следующие задачи:
В ООП есть эта вещь, называемая: принцип единой ответственности [ короткая версия ] . В основном это означает, что класс должен обрабатывать одну конкретную вещь. Вышеуказанный список составляет не менее 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 о том, как реализовать автозагрузчики. Вы можете найти его здесь . Хотя у него есть некоторые серьезные проблемы, он должен предоставить вам хорошую базу, на которой можно опираться.
Не секрет, что 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.
Это довольно распространенная ошибка, чтобы похоронить выполнение контроллеров где-то глубоко в классе маршрутизации (или классах). Это вызывает две проблемы:
Маршрутизация – это задача, которая даже в специально написанном приложении, естественно, будет тяготеть к «каркасной» части кода.
(Действительно) упрощенные версии выглядели бы так:
$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; } }