PHP MVC which URLs belong to?

I am using PHP application using MVC technique. A quick introduction for the download http://site/photos/25?options

that rewrites http://site/index.php?page=photos&id=25&options

:

  • index.php

    -> instantiates and calls the PhotoController

    object
  • PhotoController

    creates an instance PhotoView

    and loads the correct Photo

    (model) into it
  • PhotoController

    calls PhotoView->render()

    , and the header / footer views
  • PhotoView

    has access to the model Photo

    and other required model data.

PhotoView

outputs HTML and wants to hyperlink to different pages. When this view creates hyperlinks, I am confused about which layer the url should have. The options I see are described below. Please help me choose the right approach.


Model-specific URLs

Models have a method getURL()

Benefits:

  • Base class View

    can include Open Graph properties
  • PhotoView

    could do this: $folderHref = $photo->getFolder()->url

Disadvantages:

  • Some controllers do not have any appropriate model, for example LoginController

    , AdminController

    as if I was getting the URL-addresses for them?

The controller owns the URLs

Controllers have properties url

and title

. To create a link, the presenting controller creates target controllers and navigates to the view. The view refers to url

and title

from the passed object.

Benefits:

  • Each url maps directly to the controller (s index.php

    ), so the reverse seems clean.

Disadvantages:

  • A view that needs URLs for other views would require one of the following ugly hacks:

    • Unbound controller request $folderHref = Controllers\FolderController::urlForFolder($photo->getFolder())

    • Views knowing too much about their controllers $folderHref = $photo->getFolder()->getControllerAndSetURL()->getURL()

    • Excessive link between representation and its controller $folderHref = $this->delegate->getURLForFolder($photo->getFolder())

      and $adminHref = $this->delegate->getURLForAdminHref()

      with delegate

      a variety of methods


All URL owners

The base class OpenGraphObject

is the parent class for Controllers

AND Models

and has a methodgetURL()

Controllers

and Models

only implement this if it can be run with no arguments. (For example, it PhotoController

returns NULL because the URL depends on what will be displayed Photo

).

Benefits:

  • Every advantage is higher

disadvantages

  • Confusion
  • Revoking my programming license
+3


source to share


1 answer


To separate concerns, I would break it down like this:

/**
 * Responsible for processing and rendering user output
 */
interface View {

    public function render();
}

/**
 * Used just to further formalize the messages between Photo and View
 */
interface PhotoView extends View {

    public function setPhotoData($id, $title, $link);
}

/**
 * Among other things knows how to generate URLs to routes
 */
interface Router {

    /**
     * Returns the URL to route based on route name
     * @param string $routeName
     * @param array $params
     */
    public function urlTo($routeName, $params = null);
}

      

I'm not going to implement every class, there are many options you can use for example for templating engine and router.

An example of a controller for displaying an id based image could be:

class PhotoController {

    public function displayAction($photoId) {
        $photo = Photo::findById($photoId);

        $templateFile = $this->pathTo('photo-template.php');

        //can be a singleton for example, it holds all the routes for the application
        //and knows how to render them based on the name and parameters
        $router = AppRouter::instance();

        return $photo->render(new ConcretePhotoView($templateFile, $router));
    }

}

      

In this example, the photo should be able to fetch itself from the DB and then display itself in the PhotoView:

class Photo {

    private $id;
    private $title;
    private $sourceLink;

    //other methods...

    public function render(PhotoView $view) {
        $view->setPhotoData($this->id, $this->title, $this->sourceLink);

        return $view->render();
    }

}

      



Let's say the HTML template photo-template.php

looks like this:

<a href="<?= $userLogin['href'] ?>"><?= $userLogin['label'] ?></a>
<a href="<?= $userRegister['href'] ?>"><?= $userRegister['label'] ?></a>
<div>
    <img src="<?= $photo['src'] ?>" title="<?= $photo['title'] ?>" />
    <a href="<?= $photoLink['href'] ?>"><?= $photoLink['label'] ?></a>
</div>

      

It should be clear enough what variables are required for this template. No logic in it, just pure display, although some basic control would be fine.

All that's left is the ConcretePhotoView code that will use the provided template file and router to adapt data between the Photo domain object and the specific html template:

class ConcretePhotoView implements PhotoView {

    private $templateEngine;
    private $router;

    public function __construct($templateFile, Router $router) {
        $this->templateEngine = new SomeTemplateEngine($templateFile);
        $this->router = $router;
    }

    public function render() {
        //router has a route userLogin with no params
        $this->templateEngine->assign('userLogin', array(
            'href' => $this->router->urlTo('userLogin'),
            'label' => 'Login'
        ));

        $this->templateEngine->assign('userRegister', array(
            //router has a route named userRegister that requires no params
            'href' => $this->router->urlTo('userRegister'),
            'label' => 'Register'
        ));

        return $this->templateEngine->render();
    }

    public function setPhotoData($id, $title, $link) {
        $this->templateEngine->assign('photo', array(
            'src' => $link,
            'title' => $title
        ));

        $this->templateEngine->assign('photoLink', array(
            //router has a route named photo that requires an id
            'href' => $this->router->urlTo('photo', array('id' => $id)),
            'label' => $title
        ));
    }

}

      

Finally, to answer the question "Who owns the URLs?" - Router. This knowledge is encapsulated and specific to the application configuration. Other objects use it to create links.

0


source







All Articles