简体   繁体   中英

Routing issues with subcontroller structure in Phalcon

I've implemented a router , securityplugin (for ACL) and notfoundplugin .

I want my site to be set up with a subcontroller structure: Link

The website is set up in the following main parts:

  • Index
  • Admin
    • Tickets
  • Account

The issues I have:

When calling: domain/admin/actionofadmin

Returns custom 404: "Page doesn't exist" instead of the normal action result

When calling: domain/admin/actionofadmin. (<-- mind the dot at the end)

Returns index action of index controller instead of the normal action result

Why does the router return theses results? How can they be fixed?

Extra questions:

  • How does Phalcon know where to find a view and link it to the correct controller? Example: A dashboardController resides in a folder "admin" inside the folder "controllers".

  • How does Phalcon know that in the SecurityPlugin ACL it needs to search for the correct controller while it doesn't get a namespace? Example: When I want to allow controller Tickets of namespace app\\controllers\\admin to be only viewed by admin users.

Extra information:

  • Phalcon 3.0.3
  • PHP 5.6

Let me know if you need any more information / files or if I should post it somewhere else for ease of reading.

Files:

/app/controllers/AdminController.php

<?php

namespace Ontrack\Controllers;

class AdminController extends ControllerBase
{

    public function indexAction(){

    }

    public function testAction(){
      echo "test";
    }

}

/app/config/services.php Excerpt

//This makes sure the routes are correctly handled for authorized/unauthorized
/**
 * MVC dispatcher
 */
$di->set("dispatcher", function () use ($di) {
  // Create an events manager
   $eventsManager = $di->getShared('eventsManager');

   /**
   *Check if the user is allowed to access certain action using the SecurityPlugin
   *Listen for events produced in the dispatcher using the Security plugin
   */
   $eventsManager->attach("dispatch:beforeDispatch", new SecurityPlugin());

  // Handle exceptions and not-found exceptions using NotFoundPlugin
   $eventsManager->attach("dispatch:beforeException", new NotFoundPlugin());

   $dispatcher = new Dispatcher();
   $dispatcher->setDefaultNamespace('Ontrack\Controllers');

   // Assign the events manager to the dispatcher
   $dispatcher->setEventsManager($eventsManager);

   return $dispatcher;
  }
);

/app/config/loader.php

<?php

$loader = new \Phalcon\Loader();

/**
 * We're a registering a set of directories taken from the configuration file
 */
$loader->registerDirs(
    [
        $config->application->controllersDir,
        $config->application->modelsDir,
        $config->application->pluginsDir
    ]
)->register();

$loader->registerNamespaces(
    [
        'Ontrack\Controllers' => APP_PATH . '/controllers/',
        'Ontrack\Controllers\Admin' => APP_PATH . '/controllers/admin',
        'Ontrack\Models' => APP_PATH . '/models/'
    ]
)->register();

/app/config/routes.php

<?php
$router = new Phalcon\Mvc\Router(false);
$router->setDefaults(
    [
        "controller" => "index",
        "action"     => "index",
    ]
);
$router->add('/:controller/:action/:params', [
    'namespace'  => 'Ontrack\Controllers',
    'controller' => 1,
    'action'     => 2,
    'params'     => 3,
]);
$router->add('/:controller/:action', [
    'namespace'  => 'Ontrack\Controllers',
    'controller' => 1,
    'action'     => 2,
]);
$router->add('/:controller', [
    'namespace'  => 'Ontrack\Controllers',
    'controller' => 1,
]);
$router->add('/admin/:controller/:action/:params', [
    'namespace'  => 'Ontrack\Controllers\Admin',
    'controller' => 1,
    'action'     => 2,
    'params'     => 3,
]);
$router->add('/admin/:controller/:action', [
    'namespace'  => 'Ontrack\Controllers\Admin',
    'controller' => 1,
    'action'     => 2,
]);
$router->add('/admin/:controller', [
    'namespace'  => 'Ontrack\Controllers\Admin',
    'controller' => 1,
]);
$router->removeExtraSlashes(true);
return $router;

/app/plugins/SecurityPlugin.php

<?php

use Phalcon\Acl;
use Phalcon\Acl\Role;
use Phalcon\Acl\Adapter\Memory as AclList;
use Phalcon\Acl\Resource;
use Phalcon\Events\Event;
use Phalcon\Mvc\User\Plugin;
use Phalcon\Mvc\Dispatcher;

class SecurityPlugin extends Plugin
{

    /**
     * Returns an existing or new access control list
     *
     * @returns AclList
     */
    public function getAcl()
    {
        if (!isset($this->persistent->acl)) {
            $acl = new AclList();
            $acl->setDefaultAction(Acl::DENY);

            // Register roles
            $roles = [
        'admins' => new Role(
          'admins',
          'Website administrators'
        ),
                'users'  => new Role(
                    'users',
                    'Member privileges, granted after sign in.'
                ),
                'guests' => new Role(
                    'guests',
                    'Anyone browsing the site who is not signed in is considered to be a "Guest".'
                )
            ];
            foreach ($roles as $role) {
                $acl->addRole($role);
            }

            //Private area resources
            $privateResources = array(
                'account'    => array('*')
            );

      $privateResourcesAdmin = array(
                'admin'     => array('*'),
                'tickets'     => array('*')
            );

      //Public area resources
            $publicResources = array(
                'index'       => array('*'),
                'register'    => array('*'),
                'errors'      => array('show401', 'show404', 'show500'),
                'register'    => array('*'),
                'login'       => array('*'),
                'logout'            => array('*')
            );

            foreach ($privateResources as $resource => $actions) {
                $acl->addResource(new Resource($resource), $actions);
            }

      foreach ($privateResourcesAdmin as $resource => $actions) {
        $acl->addResource(new Resource($resource), $actions);
      }

            foreach ($publicResources as $resource => $actions) {
                $acl->addResource(new Resource($resource), $actions);
            }

            //Grant access to public areas to users, admins and guests
            foreach ($roles as $role) {
                foreach ($publicResources as $resource => $actions) {
                    foreach ($actions as $action){
                        $acl->allow($role->getName(), $resource, $action);
                    }
                }
            }

            //Grant access to private area to role Users
            foreach ($privateResources as $resource => $actions) {
                foreach ($actions as $action){
                    $acl->allow('users', $resource, $action);
                }
            }

      foreach ($privateResourcesAdmin as $resource => $actions) {
                foreach ($actions as $action){
                    $acl->allow('admins', $resource, $action);
                }
            }

            //The acl is stored in session, APC would be useful here too
            $this->persistent->acl = $acl;
        }
        return $this->persistent->acl;
    }

    /**
     * This action is executed before execute any action in the application
     *
     * @param Event $event
     * @param Dispatcher $dispatcher
     * @return bool
     */
    public function beforeDispatch(Event $event, Dispatcher $dispatcher){
        $auth = $this->session->has('auth');

        if (!$auth){
            $role = 'guests';
        } else {
      $authSession = $this->session->get("auth");
      if($authSession['account_type'] == 99){
        $role = 'admins';
      } else {
        $role = 'users';
      }
        }

        $controller = $dispatcher->getControllerName();
        $action = $dispatcher->getActionName();
        $acl = $this->getAcl();

        if (!$acl->isResource($controller)) {
            $dispatcher->forward([
                'controller' => 'errors',
                'action'     => 'show404'
            ]);
            return false;
        }

        $allowed = $acl->isAllowed($role, $controller, $action);

        if (!$allowed) {
            if($controller === 'admin'){
                $dispatcher->forward(array(
                    'controller' => 'errors',
                    'action'     => 'show404'
                ));
            } else {
                $dispatcher->forward(array(
                    'controller' => 'errors',
                    'action'     => 'show401'
                ));
            }
            return false;
        }
    }
}

/app/plugins/NotFoundPlugin.php

<?php

use Phalcon\Events\Event;
use Phalcon\Mvc\User\Plugin;
use Phalcon\Dispatcher;
use Phalcon\Mvc\Dispatcher\Exception as DispatcherException;
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
/**
 * NotFoundPlugin
 *
 * Handles not-found controller/actions
 */
class NotFoundPlugin extends Plugin
{
    /**
     * This action is executed before execute any action in the application
     *
     * @param Event $event
     * @param MvcDispatcher $dispatcher
     * @param Exception $exception
     * @return boolean
     */
    public function beforeException(Event $event, MvcDispatcher $dispatcher, Exception $exception)
    {
        error_log($exception->getMessage() . PHP_EOL . $exception->getTraceAsString());
        if ($exception instanceof DispatcherException) {
            switch ($exception->getCode()) {
                case Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
                case Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
                    $dispatcher->forward(array(
                        'controller' => 'errors',
                        'action' => 'show404'
                    ));
                    return false;
            }
        }
        $dispatcher->forward(array(
            'controller' => 'errors',
            'action'     => 'show500'
        ));
        return false;
    }
}

When calling: domain/admin/actionofadmin (as i understand domain is for example www.google.pl ) phalcon is expecting ActionofadminController according to your routes. Are you sure there is such controller? Don't sure why with with dot it's hitting index controller and index action though.

How does Phalcon know where to find a view and link it to the correct controller? Example: A dashboardController resides in a folder "admin" inside the folder "controllers".

It's getting this info from dispatcher. Mvc application if you don't render view your self is implicit automatically rendering. Check this source: https://github.com/phalcon/cphalcon/blob/master/phalcon/mvc/application.zep#L348 And other view classes.

About Acl Plugin - it doesn't check for namespace at all. So if you have two controllers with same name but other namespace this wil cause obviously a problem. You just need to change security plugin to your needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM