I'm using PHP MVC and I've written below code to Create URL & load core controller. My URL Format is like this - "/controller/method/params". Now I'm facing some issue with it if I try opening a specific Nav Tab by passing #id of the tab in the URL, It is ignoring that part. I want if I pass the Id of a tab then it should open that active tab. Any help will be highly appreciated.
<?php
/*
* App Core Class
* Creates URL & loads core controller
* URL FORMAT - /controller/method/params
*/
class Core
{
protected $currentController = 'Pages';
protected $currentMethod= 'index';
protected $params = [];
public function __construct()
{
//print_r($this->getUrl());
$url = $this->getUrl();
if (empty($url) || !in_array("user_images", $url)){
//Look in controller for first value
if (file_exists('../app/controllers/'. ucwords($url[0]) . '.php' )) {
//if exists, set as controller
$this->currentController = ucwords($url[0]);
//Unset 0 Index
unset($url[0]);
}
require_once '../app/controllers/' . $this->currentController . '.php';
//instantiate controller class
$this->currentController = new $this->currentController;
//check for second part of the url
if (isset($url[1])) {
if (method_exists($this->currentController, $url[1])) {
$this->currentMethod = $url[1];
unset($url[1]);
}
}
//echo $this->currentMethod;
// Get Params
$this->params = $url ? array_values($url) : [];
//call a callback with array of params
call_user_func_array([$this->currentController, $this->currentMethod],
$this->params);
}
}
public function getUrl(){
if (isset($_GET['url'])) {
$url = rtrim($_GET['url'],'/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/',$url);
return $url;
}
}
}
What changes I can do in the getUrl method?
The # is only used on the client side, the browser, as an anchor point to an element in the page. Also, it can be used on a JavaScript context to work with tabs etc.
It is not handled on server side, you should just sent parameters: ?tab=id
You can than access the tab parameter in your server side script.
Related
While upgrading to PHP7, I encountered these problem.
The issue is am trying to create codes that I can reuse like a mini-framework and the RUN function where the problem is used to load the relevant template and supplying the variables. It complains about
undefined index
of these 2 variables
$controller = $routes[$this->route][$this->method]['controller'];
$action = $routes[$this->route][$this->method]['action'];
and it also complained about this line
$page = $controller->$action();
which displayed
Fatal error: Uncaught Error: Method name must be a string in...
public function run() {
$routes = $this->routes->getRoutes();
$authentication = $this->routes->getAuthentication();
if (isset($routes[$this->route]['login']) && !$authentication->isLoggedIn()) {
header('location: /login/error');
}
else if (isset($routes[$this->route]['permissions']) && !$this->routes->checkPermission($routes[$this->route]['permissions'])) {
header('location: /login/permissionserror');
}
else {
$controller = $routes[$this->route][$this->method]['controller'];
$action = $routes[$this->route][$this->method]['action'];
$page = $controller->$action();
$title = $page['title'];
if (isset($page['variables'])) {
$output = $this->loadTemplate($page['template'], $page['variables']);
}
else {
$output = $this->loadTemplate($page['template']);
}
echo $this->loadTemplate('layout.html.php', ['loggedIn' => $authentication->isLoggedIn(),
'output' => $output,
'title' => $title
]);
}
This is the index.php
try {
include __DIR__ . '/../includes/autoload.php';
$route = ltrim(strtok($_SERVER['REQUEST_URI'], '?'), '/');
$entryPoint = new \Ninja\EntryPoint($route, $_SERVER['REQUEST_METHOD'], new \Ijdb\IjdbRoutes());
$entryPoint->run();
}
catch (PDOException $e) {
$title = 'An error has occurred';
$output = 'Database error: ' . $e->getMessage() . ' in ' .
$e->getFile() . ':' . $e->getLine();
include __DIR__ . '/../templates/layout.html.php';
}
The code is much, so, I can't display the whole code here since am using MVC pattern, but if there is anything you still want to know, I will gladly post it here
This code is runnable in php 7.2.7 (MAMP and LAMP), your way of dynamic function calling is invalid and your two variables are empty. This is not exact as yours but you can take logic form this demo.
Ok i am just providing a very simple example of reflection with mapping url to class along with functions. I make folder structure like below-
Here .htaccess is used to redirect all the url to index.php (if no file exists).
index.php include all code that could initialize code(for now only three files were there - uri.php, urlMapping.php and actions.php)
URI.php - have function that provide values like basepath, baseurl, uri
urlMappig.php - that allows you to provide which url hit which class along with method
actions.php will call dynamic class and function (reflection)
now look into code of index.php
<?php
include_once('URI.php');
include_once('urlMapping.php');
include_once('actions.php');
?>
aNow code insise uri.php file -
<?php
// all function should be accessible to all file loaded now
// return full url
function full_url (){
return (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
}
// returns current directory
function directory() {
$parts = explode("/", __DIR__);
return $parts[count($parts)-1];
}
// return base url
function base_url(){
$dir = directory();
return substr(full_url(), 0, strpos(full_url(), $dir)+strlen($dir));
}
// return uri
function uri() {
return substr(full_url(), strlen(base_url())+1);
}
?>
Now code in urlMapping.php
Note - file name and name of the class must be same as you map url in
this file so that you can make call to dynamic classes and function on actions.php. If don't this will not work
<?php
// this $urlMap will be accessed in actions.php
$urlMap = [
// here we use only uri so .. https://example.com/login hit LoginController class along with login function, this is just only the mapping
'login' => ['class'=>'LoginController',
'function'=>'login'],
];
?>
Now actions.php code
<?php
// if call is not like example.com/ means no uri is there
if(uri()!='')
{
// if your uri exists in route collection or urlmapping collection then make call to your dynamic class and methods
if(array_key_exists(uri(), $urlMap))
{
// include the class file dynamically from controllers folder
include_once('controllers/'.$urlMap[uri()]['class'].'.php');
// making references for dynamic class
$controlerObject = new $urlMap[uri()]['class']();
// call function dynaically from the referece
$controlerObject->{$urlMap[uri()]['function']}();
}
else
{
// you can make 404 page not
echo 'No routing found';
}
}
// call for home page
else
{
include_once('index.html.php');
}
?>
Now controllres/LoginController.php class,
Note - As mentioned above file name and name of the class as in urlMapping.php
<?php
class LoginController{
public function login()
{
// .... something your code goes here
echo 'hello from the login function of login controller';
}
//...... other function you can define
}
?>
Before calling $page = $controller->$action(); check if controller and action are exists:
if (isset($routes[$this->route][$this->method]['controller'])
&& isset($routes[$this->route][$this->method]['action'])) {
$controller = $routes[$this->route][$this->method]['controller'];
$action = $routes[$this->route][$this->method]['action'];
$page = $controller->$action();
// ...
} else {
// return 404 Page not found
}
Im using PHP MVC for my site and I have an issue with routing.
When I go to the index (front page), I use http://www.example.com or http://www.example.com/index.
When I go to the contact page, I use http://www.example.com/contact.
When I go to the services or about pages, I use http://www.example.com/content/page/services or http://www.example.com/content/page/about.
My index and contact pages have their own controllers because they are static pages. But the services and about pages are pulled from my db, thus dynamic. So I created a controller, named it content and just pass the parameters needed to get whatever page I want.
I want to make my URLs more consistent. If I go to the services or about pages, I want to use http://www.example.com/services or http://www.example.com/about.
How can I change my routing to meet this requirement? I ultimately would like to be able to create pages in my db, and then pull the page with a URL that looks like it has its own controller. Instead of having to call the content controller to get it to work.
Below are my controllers and what methods they contain, as well as my routing code.
Controllers:
IndexController
function: index
ContentController
function: page
function: sitemap
ContactController
function: index
function: process
Routing
class Application
{
// #var mixed Instance of the controller
private $controller;
// #var array URL parameters, will be passed to used controller-method
private $parameters = array();
// #var string Just the name of the controller, useful for checks inside the view ("where am I ?")
private $controller_name;
// #var string Just the name of the controller's method, useful for checks inside the view ("where am I ?")
private $action_name;
// Start the application, analyze URL elements, call according controller/method or relocate to fallback location
public function __construct()
{
// Create array with URL parts in $url
$this->splitUrl();
// Check for controller: no controller given ? then make controller = default controller (from config)
if (!$this->controller_name) {
$this->controller_name = Config::get('DEFAULT_CONTROLLER');
}
// Check for action: no action given ? then make action = default action (from config)
if (!$this->action_name OR (strlen($this->action_name) == 0)) {
$this->action_name = Config::get('DEFAULT_ACTION');
}
// Rename controller name to real controller class/file name ("index" to "IndexController")
$this->controller_name = ucwords($this->controller_name) . 'Controller';
// Check if controller exists
if (file_exists(Config::get('PATH_CONTROLLER') . $this->controller_name . '.php')) {
// Load file and create controller
// example: if controller would be "car", then this line would translate into: $this->car = new car();
require Config::get('PATH_CONTROLLER') . $this->controller_name . '.php';
$this->controller = new $this->controller_name();
// Check for method: does such a method exist in the controller?
if (method_exists($this->controller, $this->action_name)) {
if (!empty($this->parameters)) {
// Call the method and pass arguments to it
call_user_func_array(array($this->controller, $this->action_name), $this->parameters);
} else {
// If no parameters are given, just call the method without parameters, like $this->index->index();
$this->controller->{$this->action_name}();
}
} else {
header('location: ' . Config::get('URL') . 'error');
}
} else {
header('location: ' . Config::get('URL') . 'error');
}
}
// Split URL
private function splitUrl()
{
if (Request::get('url')) {
// Split URL
$url = trim(Request::get('url'), '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
// Put URL parts into according properties
$this->controller_name = isset($url[0]) ? $url[0] : null;
$this->action_name = isset($url[1]) ? $url[1] : null;
// Remove controller name and action name from the split URL
unset($url[0], $url[1]);
// rebase array keys and store the URL parameters
$this->parameters = array_values($url);
}
}
}
In order to do this you should map your urls to controllers, check following example:
// route mapping 'route' => 'controller:method'
$routes = array(
'/service' => 'Content:service'
);
also controller can be any php callable function.
Answer Version 2:
Brother in the simplest mode, let's say you have an entity like below:
uri: varchar(255), title: varchar(255), meta_tags: varchar(500), body: text
and have access to StaticPageController from www.example.com/page/ url and what ever it comes after this url will pass to controller as uri parameter
public function StaticPageController($uri){
// this can return a page entity
// that contains what ever a page needs.
$page = $pageRepository->findByUri($uri)
// pass it to view layer
$this->renderView('static_page.phtml', array('page' => $page));
}
I hope this helps.
I´m making a "very" simple MVC framework in order to learn, however I have trouble getting other pages than the index page to show. In views folder I have 2 files one index.php and one register.php that I´m trying on.
I have tried various ways but can´t get my head around it. I know it is probably best to put different controller classes in different files and maybe a loader controller page but I´m a beginner with php so would like to make it as simple as possible for me...
Any help appriciated!
I have a index.php as a landing file in the root folder to bind everything together:
<?php
/* index.php
*
*/
require_once 'model/load.php';
require_once 'controller/main.php';
new mainController();
In the controller folder i have a file called main.php:
<?php
/* controller/main.php
*
*/
class mainController
{
public $load;
public function __construct()
{
$urlValues = $_SERVER['REQUEST_URI'];
$this->urlValues = $_GET;
//index page
if ($this->urlValues['controller'] == "") {
$indexPage = array("key" => "Hello");
$this->load = new load();
$this->load->view('index.php', $indexPage);
}
//register page
if ($this->urlValues['controller'] == "register.php") {
$registerPage = array("key" => "Register");
$this->load = new load();
$this->load->view('register.php', $registerPage);
}
}
}
And then I have a file called load.php in the model folder:
<?php
/* model/load.php
*
*/
class load
{
/* This function takes parameter
* $file_name and match with file in views.
*/
function view($file_name, $data = null)
{
if (is_readable('views/' . $file_name)) {
if (is_array($data)) {
extract($data);
}
require 'views/' . $file_name;
} else {
echo $this->file;
die ('404 Not Found');
}
}
}
In your mainController class you don't have property with the name urlValues, but you use it: $this->urlValues = $_GET;. And what is more you have local variable with the same name, that you don't use: $urlValues = $_SERVER['REQUEST_URI'];
And how you URL for register.php looks like?
Right now, I'm trying to improve my MVC router dispatch function. My main problem is that users can attempt to access objects and if it fails, obviously, a blank page is returned.
For example, if I have an object named error and one of the class methods is setType(), a user can enter error/settype is the URI.
All urls are written to index.php?url=$1
$url = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
$controller = !empty($url[0]) ? $url[0] : 'home';
$method = !empty($url[1]) ? $url[1] : 'index';
$params = !empty($url[2]) ? $url[2] : $_POST;
if (class_exists($controller)){
$dispatchedController = new $controller;
if (! method_exists($controller, $method)){
/**
* Error handling
**/
}
return $dispatchedController->$method($params);
} else{
/**
* Error handling
**/
}
How could I improve my router? Providing a whitelist or blacklist isn't very practical, I would think.
I'm having a small problem when trying to flash a message and redirect the user back to the previous page in Symfony 2.
I have a very simple CRUD. When new, or edit, i want to flash a message if something goes wrong in the respective create/update methods:
User --GET--> new
new --POST--> create (fails)
--REDIRECT--> new (with flash message)
I'm doing the following:
$this->container->get('session')->setFlash('error', 'myerror');
$referer = $this->getRequest()->headers->get('referer');
return new RedirectResponse($referer);
However, it's not redirecting to the correct referrer! Even though the value of referrer is correct (eg.: http://localhost/demo/2/edit/) It redirects to the index. Why?
This is an alternative version of Naitsirch and Santi their code. I realized a trait would be perfect for this functionality. Also optimized the code somewhat. I preferred to give back all the parameters including the slugs, because you might need those when generating the route.
This code runs on PHP 5.4.0 and up. You can use the trait for multiple controllers of course. If you put the trait in a seperate file make sure you name it the same as the trait, following PSR-0.
<?php
trait Referer {
private function getRefererParams() {
$request = $this->getRequest();
$referer = $request->headers->get('referer');
$baseUrl = $request->getBaseUrl();
$lastPath = substr($referer, strpos($referer, $baseUrl) + strlen($baseUrl));
return $this->get('router')->getMatcher()->match($lastPath);
}
}
class MyController extends Controller {
use Referer;
public function MyAction() {
$params = $this->getRefererParams();
return $this->redirect($this->generateUrl(
$params['_route'],
[
'slug' => $params['slug']
]
));
}
}
For symfony 3.0,flash message with redirection back to previous page,this can be done in controller.
$request->getSession()
->getFlashBag()
->add('notice', 'success');
$referer = $request->headers->get('referer');
return $this->redirect($referer);
The message from Naitsirch presented in the next url:
https://github.com/symfony/symfony/issues/2951
Seems a good solution for that you need:
public function getRefererRoute()
{
$request = $this->getRequest();
//look for the referer route
$referer = $request->headers->get('referer');
$lastPath = substr($referer, strpos($referer, $request->getBaseUrl()));
$lastPath = str_replace($request->getBaseUrl(), '', $lastPath);
$matcher = $this->get('router')->getMatcher();
$parameters = $matcher->match($lastPath);
$route = $parameters['_route'];
return $route;
}
Then with a redirect:
public function yourFunctionAction()
{
$ruta = $this->getRefererRoute();
$locale = $request->get('_locale');
$url = $this->get('router')->generate($ruta, array('_locale' => $locale));
$this->getRequest()->getSession()->setFlash('notice', "your_message");
return $this->redirect($url);
}
This works for me:
$this->redirect($request->server->get('HTTP_REFERER'));
I have similar functionality on my site. It is multilingual. Articles exists only in a single locale. When the user will try to switch to other locale, it should redirect back to previous page and flash message that that page/article doesn't exist on the requested locale.
/en/article/3 -> /fr/article/3 (404) -> Redirect(/en/article/3)
Here is my version of the script that works well on dev and prod environments:
$referer = $request->headers->get('referer')
// 'https://your-domain.com' or 'https://your-domain.com/app_dev.php'
$base = $request->getSchemeAndHttpHost() . $request->getBaseUrl();
// '/en/article/3'
$path = preg_replace('/^'. preg_quote($base, '/') .'/', '', $referer);
if ($path === $referer) {
// nothing was replaced. referer is an external site
} elseif ($path === $request->getPathInfo()) {
// current page and referer are the same (prevent redirect loop)
} else {
try {
// if this will throw an exception then the route doesn't exist
$this->container->get('router')->match(
// '/en/hello?foo=bar' -> '/en/hello'
preg_replace('/\?.*$/', '', $path)
);
// '/app_dev.php' . '/en/article/3'
$redirectUrl = $request->getBaseUrl() . $path;
return new RedirectResponse($redirectUrl);
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {}
}
I just set up a simple app, and it seems to work fine. My createAction() looks like this:
public function createAction()
{
$entity = new Pokemon();
$request = $this->getRequest();
$form = $this->createForm(new PokemonType(), $entity);
$form->bindRequest($request);
if ($entity->getName() == "pikachu")
{
$this->container->get("session")->setFlash("error", "Pikachu is not allowed");
$url = $this->getRequest()->headers->get("referer");
return new RedirectResponse($url);
}
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('pokemon_show', array('id' => $entity->getId())));
}
return $this->render('BulbasaurBundle:Pokemon:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView()
));
}
The flow goes:
User navigates to /new
User enters invalid option of "pikachu"
User clicks submit (POSTs to /create)
Application rejects the entry, adds flash message, and redirects back to /new
User sees /new with the flash message
A few things to check:
Is your route for /demo/{entityId}/edit actually working? (i.e. if you enter it in the browser, does it actually go to where you expect it to?)
Are you chaining together different redirects/forwards? I've noticed that I get unexpected (but correct) behavior when I have a controller that redirects to a URL, and the controller responsible for that URL also redirects somewhere else. I've fixed this issue by using forwards instead.
That said, if all else fails, you could just use the controller's redirect() method to manage the redirect:
public function createAction()
{
...
return $this->redirect($this->generateUrl("pokemon_new"));
...
}
Here you go, declare this as a service and it will return referer to you wherever and whenever you need it. No traits, no weird dependencies.
class Referer
{
/** #var RequestStack */
private $requestStack;
/** #var RouterInterface */
private $router;
public function __construct(RequestStack $requestStack, RouterInterface $router)
{
$this->requestStack = $requestStack;
$this->router = $router;
}
public function getReferer() : string
{
$request = $this->requestStack->getMasterRequest();
if (null === $request)
{
return '';
}
//if you're happy with URI (and most times you are), just return it
$uri = (string)$request->headers->get('referer');
//but if you want to return route, here you go
try
{
$routeMatch = $this->router->match($uri);
}
catch (ResourceNotFoundException $e)
{
return '';
}
$route = $routeMatch['_route'];
return $route;
}
}
seems like you need to have a payload for your redirect to point to. it seems like obscure concept code to me. I would also advise you to make sure your configuration files point to the correct redirect code snippet. Check your server access file to make sure it has redirects enabled also.