1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 0.2.9
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Controller;
16:
17: use Cake\Controller\Exception\MissingActionException;
18: use Cake\Datasource\ModelAwareTrait;
19: use Cake\Event\Event;
20: use Cake\Event\EventDispatcherInterface;
21: use Cake\Event\EventDispatcherTrait;
22: use Cake\Event\EventListenerInterface;
23: use Cake\Http\Response;
24: use Cake\Http\ServerRequest;
25: use Cake\Log\LogTrait;
26: use Cake\ORM\Locator\LocatorAwareTrait;
27: use Cake\Routing\RequestActionTrait;
28: use Cake\Routing\Router;
29: use Cake\Utility\MergeVariablesTrait;
30: use Cake\View\ViewVarsTrait;
31: use LogicException;
32: use ReflectionClass;
33: use ReflectionException;
34: use ReflectionMethod;
35: use RuntimeException;
36:
37: /**
38: * Application controller class for organization of business logic.
39: * Provides basic functionality, such as rendering views inside layouts,
40: * automatic model availability, redirection, callbacks, and more.
41: *
42: * Controllers should provide a number of 'action' methods. These are public
43: * methods on a controller that are not inherited from `Controller`.
44: * Each action serves as an endpoint for performing a specific action on a
45: * resource or collection of resources. For example adding or editing a new
46: * object, or listing a set of objects.
47: *
48: * You can access request parameters, using `$this->request`. The request object
49: * contains all the POST, GET and FILES that were part of the request.
50: *
51: * After performing the required action, controllers are responsible for
52: * creating a response. This usually takes the form of a generated `View`, or
53: * possibly a redirection to another URL. In either case `$this->response`
54: * allows you to manipulate all aspects of the response.
55: *
56: * Controllers are created by `Dispatcher` based on request parameters and
57: * routing. By default controllers and actions use conventional names.
58: * For example `/posts/index` maps to `PostsController::index()`. You can re-map
59: * URLs using Router::connect() or RouterBuilder::connect().
60: *
61: * ### Life cycle callbacks
62: *
63: * CakePHP fires a number of life cycle callbacks during each request.
64: * By implementing a method you can receive the related events. The available
65: * callbacks are:
66: *
67: * - `beforeFilter(Event $event)`
68: * Called before each action. This is a good place to do general logic that
69: * applies to all actions.
70: * - `beforeRender(Event $event)`
71: * Called before the view is rendered.
72: * - `beforeRedirect(Event $event, $url, Response $response)`
73: * Called before a redirect is done.
74: * - `afterFilter(Event $event)`
75: * Called after each action is complete and after the view is rendered.
76: *
77: * @property \Cake\Controller\Component\AuthComponent $Auth
78: * @property \Cake\Controller\Component\CookieComponent $Cookie
79: * @property \Cake\Controller\Component\CsrfComponent $Csrf
80: * @property \Cake\Controller\Component\FlashComponent $Flash
81: * @property \Cake\Controller\Component\PaginatorComponent $Paginator
82: * @property \Cake\Controller\Component\RequestHandlerComponent $RequestHandler
83: * @property \Cake\Controller\Component\SecurityComponent $Security
84: * @method bool isAuthorized($user)
85: * @link https://book.cakephp.org/3.0/en/controllers.html
86: */
87: class Controller implements EventListenerInterface, EventDispatcherInterface
88: {
89: use EventDispatcherTrait;
90: use LocatorAwareTrait;
91: use LogTrait;
92: use MergeVariablesTrait;
93: use ModelAwareTrait;
94: use RequestActionTrait;
95: use ViewVarsTrait;
96:
97: /**
98: * The name of this controller. Controller names are plural, named after the model they manipulate.
99: *
100: * Set automatically using conventions in Controller::__construct().
101: *
102: * @var string
103: */
104: protected $name;
105:
106: /**
107: * An array containing the names of helpers this controller uses. The array elements should
108: * not contain the "Helper" part of the class name.
109: *
110: * Example:
111: * ```
112: * public $helpers = ['Form', 'Html', 'Time'];
113: * ```
114: *
115: * @var array
116: * @link https://book.cakephp.org/3.0/en/controllers.html#configuring-helpers-to-load
117: *
118: * @deprecated 3.0.0 You should configure helpers in your AppView::initialize() method.
119: */
120: public $helpers = [];
121:
122: /**
123: * An instance of a \Cake\Http\ServerRequest object that contains information about the current request.
124: * This object contains all the information about a request and several methods for reading
125: * additional information about the request.
126: *
127: * Deprecated 3.6.0: The property will become protected in 4.0.0. Use getRequest()/setRequest instead.
128: *
129: * @var \Cake\Http\ServerRequest
130: * @link https://book.cakephp.org/3.0/en/controllers/request-response.html#request
131: */
132: public $request;
133:
134: /**
135: * An instance of a Response object that contains information about the impending response
136: *
137: * Deprecated 3.6.0: The property will become protected in 4.0.0. Use getResponse()/setResponse instead.
138:
139: * @var \Cake\Http\Response
140: * @link https://book.cakephp.org/3.0/en/controllers/request-response.html#response
141: */
142: public $response;
143:
144: /**
145: * The class name to use for creating the response object.
146: *
147: * @var string
148: */
149: protected $_responseClass = Response::class;
150:
151: /**
152: * Settings for pagination.
153: *
154: * Used to pre-configure pagination preferences for the various
155: * tables your controller will be paginating.
156: *
157: * @var array
158: * @see \Cake\Controller\Component\PaginatorComponent
159: */
160: public $paginate = [];
161:
162: /**
163: * Set to true to automatically render the view
164: * after action logic.
165: *
166: * @var bool
167: */
168: protected $autoRender = true;
169:
170: /**
171: * Instance of ComponentRegistry used to create Components
172: *
173: * @var \Cake\Controller\ComponentRegistry
174: */
175: protected $_components;
176:
177: /**
178: * Array containing the names of components this controller uses. Component names
179: * should not contain the "Component" portion of the class name.
180: *
181: * Example:
182: * ```
183: * public $components = ['RequestHandler', 'Acl'];
184: * ```
185: *
186: * @var array
187: * @link https://book.cakephp.org/3.0/en/controllers/components.html
188: *
189: * @deprecated 3.0.0 You should configure components in your Controller::initialize() method.
190: */
191: public $components = [];
192:
193: /**
194: * Instance of the View created during rendering. Won't be set until after
195: * Controller::render() is called.
196: *
197: * @var \Cake\View\View
198: * @deprecated 3.1.0 Use viewBuilder() instead.
199: */
200: public $View;
201:
202: /**
203: * These Controller properties will be passed from the Controller to the View as options.
204: *
205: * @var array
206: * @see \Cake\View\View
207: * @deprecated 3.7.0 Use ViewBuilder::setOptions() or any one of it's setter methods instead.
208: */
209: protected $_validViewOptions = [
210: 'passedArgs'
211: ];
212:
213: /**
214: * Automatically set to the name of a plugin.
215: *
216: * @var string|null
217: */
218: protected $plugin;
219:
220: /**
221: * Holds all passed params.
222: *
223: * @var array
224: * @deprecated 3.1.0 Use `$this->request->getParam('pass')` instead.
225: */
226: public $passedArgs = [];
227:
228: /**
229: * Constructor.
230: *
231: * Sets a number of properties based on conventions if they are empty. To override the
232: * conventions CakePHP uses you can define properties in your class declaration.
233: *
234: * @param \Cake\Http\ServerRequest|null $request Request object for this controller. Can be null for testing,
235: * but expect that features that use the request parameters will not work.
236: * @param \Cake\Http\Response|null $response Response object for this controller.
237: * @param string|null $name Override the name useful in testing when using mocks.
238: * @param \Cake\Event\EventManager|null $eventManager The event manager. Defaults to a new instance.
239: * @param \Cake\Controller\ComponentRegistry|null $components The component registry. Defaults to a new instance.
240: */
241: public function __construct(ServerRequest $request = null, Response $response = null, $name = null, $eventManager = null, $components = null)
242: {
243: if ($name !== null) {
244: $this->name = $name;
245: }
246:
247: if ($this->name === null && $request && $request->getParam('controller')) {
248: $this->name = $request->getParam('controller');
249: }
250:
251: if ($this->name === null) {
252: list(, $name) = namespaceSplit(get_class($this));
253: $this->name = substr($name, 0, -10);
254: }
255:
256: $this->setRequest($request ?: new ServerRequest());
257: $this->response = $response ?: new Response();
258:
259: if ($eventManager !== null) {
260: $this->setEventManager($eventManager);
261: }
262:
263: $this->modelFactory('Table', [$this->getTableLocator(), 'get']);
264: $plugin = $this->request->getParam('plugin');
265: $modelClass = ($plugin ? $plugin . '.' : '') . $this->name;
266: $this->_setModelClass($modelClass);
267:
268: if ($components !== null) {
269: $this->components($components);
270: }
271:
272: $this->initialize();
273:
274: $this->_mergeControllerVars();
275: $this->_loadComponents();
276: $this->getEventManager()->on($this);
277: }
278:
279: /**
280: * Initialization hook method.
281: *
282: * Implement this method to avoid having to overwrite
283: * the constructor and call parent.
284: *
285: * @return void
286: */
287: public function initialize()
288: {
289: }
290:
291: /**
292: * Get the component registry for this controller.
293: *
294: * If called with the first parameter, it will be set as the controller $this->_components property
295: *
296: * @param \Cake\Controller\ComponentRegistry|null $components Component registry.
297: *
298: * @return \Cake\Controller\ComponentRegistry
299: */
300: public function components($components = null)
301: {
302: if ($components === null && $this->_components === null) {
303: $this->_components = new ComponentRegistry($this);
304: }
305: if ($components !== null) {
306: $components->setController($this);
307: $this->_components = $components;
308: }
309:
310: return $this->_components;
311: }
312:
313: /**
314: * Add a component to the controller's registry.
315: *
316: * This method will also set the component to a property.
317: * For example:
318: *
319: * ```
320: * $this->loadComponent('Acl.Acl');
321: * ```
322: *
323: * Will result in a `Toolbar` property being set.
324: *
325: * @param string $name The name of the component to load.
326: * @param array $config The config for the component.
327: * @return \Cake\Controller\Component
328: * @throws \Exception
329: */
330: public function loadComponent($name, array $config = [])
331: {
332: list(, $prop) = pluginSplit($name);
333:
334: return $this->{$prop} = $this->components()->load($name, $config);
335: }
336:
337: /**
338: * Magic accessor for model autoloading.
339: *
340: * @param string $name Property name
341: * @return bool|object The model instance or false
342: */
343: public function __get($name)
344: {
345: $deprecated = [
346: 'name' => 'getName',
347: 'plugin' => 'getPlugin',
348: 'autoRender' => 'isAutoRenderEnabled',
349: ];
350: if (isset($deprecated[$name])) {
351: $method = $deprecated[$name];
352: deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s() instead.', $name, $method));
353:
354: return $this->{$method}();
355: }
356:
357: $deprecated = [
358: 'layout' => 'getLayout',
359: 'view' => 'getTemplate',
360: 'theme' => 'getTheme',
361: 'autoLayout' => 'isAutoLayoutEnabled',
362: 'viewPath' => 'getTemplatePath',
363: 'layoutPath' => 'getLayoutPath',
364: ];
365: if (isset($deprecated[$name])) {
366: $method = $deprecated[$name];
367: deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
368:
369: return $this->viewBuilder()->{$method}();
370: }
371:
372: list($plugin, $class) = pluginSplit($this->modelClass, true);
373: if ($class === $name) {
374: return $this->loadModel($plugin . $class);
375: }
376:
377: $trace = debug_backtrace();
378: $parts = explode('\\', get_class($this));
379: trigger_error(
380: sprintf(
381: 'Undefined property: %s::$%s in %s on line %s',
382: array_pop($parts),
383: $name,
384: $trace[0]['file'],
385: $trace[0]['line']
386: ),
387: E_USER_NOTICE
388: );
389:
390: return false;
391: }
392:
393: /**
394: * Magic setter for removed properties.
395: *
396: * @param string $name Property name.
397: * @param mixed $value Value to set.
398: * @return void
399: */
400: public function __set($name, $value)
401: {
402: $deprecated = [
403: 'name' => 'setName',
404: 'plugin' => 'setPlugin'
405: ];
406: if (isset($deprecated[$name])) {
407: $method = $deprecated[$name];
408: deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s() instead.', $name, $method));
409: $this->{$method}($value);
410:
411: return;
412: }
413: if ($name === 'autoRender') {
414: $value ? $this->enableAutoRender() : $this->disableAutoRender();
415: deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->enableAutoRender/disableAutoRender() instead.', $name));
416:
417: return;
418: }
419: $deprecated = [
420: 'layout' => 'setLayout',
421: 'view' => 'setTemplate',
422: 'theme' => 'setTheme',
423: 'autoLayout' => 'enableAutoLayout',
424: 'viewPath' => 'setTemplatePath',
425: 'layoutPath' => 'setLayoutPath',
426: ];
427: if (isset($deprecated[$name])) {
428: $method = $deprecated[$name];
429: deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
430:
431: $this->viewBuilder()->{$method}($value);
432:
433: return;
434: }
435:
436: $this->{$name} = $value;
437: }
438:
439: /**
440: * Returns the controller name.
441: *
442: * @return string
443: * @since 3.6.0
444: */
445: public function getName()
446: {
447: return $this->name;
448: }
449:
450: /**
451: * Sets the controller name.
452: *
453: * @param string $name Controller name.
454: * @return $this
455: * @since 3.6.0
456: */
457: public function setName($name)
458: {
459: $this->name = $name;
460:
461: return $this;
462: }
463:
464: /**
465: * Returns the plugin name.
466: *
467: * @return string|null
468: * @since 3.6.0
469: */
470: public function getPlugin()
471: {
472: return $this->plugin;
473: }
474:
475: /**
476: * Sets the plugin name.
477: *
478: * @param string $name Plugin name.
479: * @return $this
480: * @since 3.6.0
481: */
482: public function setPlugin($name)
483: {
484: $this->plugin = $name;
485:
486: return $this;
487: }
488:
489: /**
490: * Returns true if an action should be rendered automatically.
491: *
492: * @return bool
493: * @since 3.6.0
494: */
495: public function isAutoRenderEnabled()
496: {
497: return $this->autoRender;
498: }
499:
500: /**
501: * Enable automatic action rendering.
502: *
503: * @return $this
504: * @since 3.6.0
505: */
506: public function enableAutoRender()
507: {
508: $this->autoRender = true;
509:
510: return $this;
511: }
512:
513: /**
514: * Disable automatic action rendering.
515: *
516: * @return $this
517: * @since 3.6.0
518: */
519: public function disableAutoRender()
520: {
521: $this->autoRender = false;
522:
523: return $this;
524: }
525:
526: /**
527: * Gets the request instance.
528: *
529: * @return \Cake\Http\ServerRequest
530: * @since 3.6.0
531: */
532: public function getRequest()
533: {
534: return $this->request;
535: }
536:
537: /**
538: * Sets the request objects and configures a number of controller properties
539: * based on the contents of the request. Controller acts as a proxy for certain View variables
540: * which must also be updated here. The properties that get set are:
541: *
542: * - $this->request - To the $request parameter
543: * - $this->passedArgs - Same as $request->params['pass]
544: *
545: * @param \Cake\Http\ServerRequest $request Request instance.
546: * @return $this
547: */
548: public function setRequest(ServerRequest $request)
549: {
550: $this->request = $request;
551: $this->plugin = $request->getParam('plugin') ?: null;
552:
553: if ($request->getParam('pass')) {
554: $this->passedArgs = $request->getParam('pass');
555: }
556:
557: return $this;
558: }
559:
560: /**
561: * Gets the response instance.
562: *
563: * @return \Cake\Http\Response
564: * @since 3.6.0
565: */
566: public function getResponse()
567: {
568: return $this->response;
569: }
570:
571: /**
572: * Sets the response instance.
573: *
574: * @param \Cake\Http\Response $response Response instance.
575: * @return $this
576: * @since 3.6.0
577: */
578: public function setResponse(Response $response)
579: {
580: $this->response = $response;
581:
582: return $this;
583: }
584:
585: /**
586: * Dispatches the controller action. Checks that the action
587: * exists and isn't private.
588: *
589: * @return mixed The resulting response.
590: * @throws \ReflectionException
591: */
592: public function invokeAction()
593: {
594: $request = $this->request;
595: if (!$request) {
596: throw new LogicException('No Request object configured. Cannot invoke action');
597: }
598: if (!$this->isAction($request->getParam('action'))) {
599: throw new MissingActionException([
600: 'controller' => $this->name . 'Controller',
601: 'action' => $request->getParam('action'),
602: 'prefix' => $request->getParam('prefix') ?: '',
603: 'plugin' => $request->getParam('plugin'),
604: ]);
605: }
606: /* @var callable $callable */
607: $callable = [$this, $request->getParam('action')];
608:
609: $result = $callable(...array_values($request->getParam('pass')));
610: if ($result instanceof Response) {
611: $this->response = $result;
612: }
613:
614: return $result;
615: }
616:
617: /**
618: * Merge components, helpers vars from
619: * parent classes.
620: *
621: * @return void
622: */
623: protected function _mergeControllerVars()
624: {
625: $this->_mergeVars(
626: ['components', 'helpers'],
627: ['associative' => ['components', 'helpers']]
628: );
629: }
630:
631: /**
632: * Returns a list of all events that will fire in the controller during its lifecycle.
633: * You can override this function to add your own listener callbacks
634: *
635: * @return array
636: */
637: public function implementedEvents()
638: {
639: return [
640: 'Controller.initialize' => 'beforeFilter',
641: 'Controller.beforeRender' => 'beforeRender',
642: 'Controller.beforeRedirect' => 'beforeRedirect',
643: 'Controller.shutdown' => 'afterFilter',
644: ];
645: }
646:
647: /**
648: * Loads the defined components using the Component factory.
649: *
650: * @return void
651: */
652: protected function _loadComponents()
653: {
654: if (empty($this->components)) {
655: return;
656: }
657: $registry = $this->components();
658: $components = $registry->normalizeArray($this->components);
659: foreach ($components as $properties) {
660: $this->loadComponent($properties['class'], $properties['config']);
661: }
662: }
663:
664: /**
665: * Perform the startup process for this controller.
666: * Fire the Components and Controller callbacks in the correct order.
667: *
668: * - Initializes components, which fires their `initialize` callback
669: * - Calls the controller `beforeFilter`.
670: * - triggers Component `startup` methods.
671: *
672: * @return \Cake\Http\Response|null
673: */
674: public function startupProcess()
675: {
676: $event = $this->dispatchEvent('Controller.initialize');
677: if ($event->getResult() instanceof Response) {
678: return $event->getResult();
679: }
680: $event = $this->dispatchEvent('Controller.startup');
681: if ($event->getResult() instanceof Response) {
682: return $event->getResult();
683: }
684:
685: return null;
686: }
687:
688: /**
689: * Perform the various shutdown processes for this controller.
690: * Fire the Components and Controller callbacks in the correct order.
691: *
692: * - triggers the component `shutdown` callback.
693: * - calls the Controller's `afterFilter` method.
694: *
695: * @return \Cake\Http\Response|null
696: */
697: public function shutdownProcess()
698: {
699: $event = $this->dispatchEvent('Controller.shutdown');
700: if ($event->getResult() instanceof Response) {
701: return $event->getResult();
702: }
703:
704: return null;
705: }
706:
707: /**
708: * Redirects to given $url, after turning off $this->autoRender.
709: *
710: * @param string|array $url A string or array-based URL pointing to another location within the app,
711: * or an absolute URL
712: * @param int $status HTTP status code (eg: 301)
713: * @return \Cake\Http\Response|null
714: * @link https://book.cakephp.org/3.0/en/controllers.html#Controller::redirect
715: */
716: public function redirect($url, $status = 302)
717: {
718: $this->autoRender = false;
719:
720: if ($status) {
721: $this->response = $this->response->withStatus($status);
722: }
723:
724: $event = $this->dispatchEvent('Controller.beforeRedirect', [$url, $this->response]);
725: if ($event->getResult() instanceof Response) {
726: return $this->response = $event->getResult();
727: }
728: if ($event->isStopped()) {
729: return null;
730: }
731: $response = $this->response;
732:
733: if (!$response->getHeaderLine('Location')) {
734: $response = $response->withLocation(Router::url($url, true));
735: }
736:
737: return $this->response = $response;
738: }
739:
740: /**
741: * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
742: *
743: * Examples:
744: *
745: * ```
746: * setAction('another_action');
747: * setAction('action_with_parameters', $parameter1);
748: * ```
749: *
750: * @param string $action The new action to be 'redirected' to.
751: * Any other parameters passed to this method will be passed as parameters to the new action.
752: * @param array ...$args Arguments passed to the action
753: * @return mixed Returns the return value of the called action
754: */
755: public function setAction($action, ...$args)
756: {
757: $this->setRequest($this->request->withParam('action', $action));
758:
759: return $this->$action(...$args);
760: }
761:
762: /**
763: * Instantiates the correct view class, hands it its data, and uses it to render the view output.
764: *
765: * @param string|null $view View to use for rendering
766: * @param string|null $layout Layout to use
767: * @return \Cake\Http\Response A response object containing the rendered view.
768: * @link https://book.cakephp.org/3.0/en/controllers.html#rendering-a-view
769: */
770: public function render($view = null, $layout = null)
771: {
772: $builder = $this->viewBuilder();
773: if (!$builder->getTemplatePath()) {
774: $builder->setTemplatePath($this->_viewPath());
775: }
776:
777: if ($this->request->getParam('bare')) {
778: $builder->disableAutoLayout();
779: }
780: $this->autoRender = false;
781:
782: $event = $this->dispatchEvent('Controller.beforeRender');
783: if ($event->getResult() instanceof Response) {
784: return $event->getResult();
785: }
786: if ($event->isStopped()) {
787: return $this->response;
788: }
789:
790: if ($builder->getTemplate() === null && $this->request->getParam('action')) {
791: $builder->setTemplate($this->request->getParam('action'));
792: }
793:
794: $this->View = $this->createView();
795: $contents = $this->View->render($view, $layout);
796: $this->setResponse($this->View->getResponse()->withStringBody($contents));
797:
798: return $this->response;
799: }
800:
801: /**
802: * Get the viewPath based on controller name and request prefix.
803: *
804: * @return string
805: */
806: protected function _viewPath()
807: {
808: $viewPath = $this->name;
809: if ($this->request->getParam('prefix')) {
810: $prefixes = array_map(
811: 'Cake\Utility\Inflector::camelize',
812: explode('/', $this->request->getParam('prefix'))
813: );
814: $viewPath = implode(DIRECTORY_SEPARATOR, $prefixes) . DIRECTORY_SEPARATOR . $viewPath;
815: }
816:
817: return $viewPath;
818: }
819:
820: /**
821: * Returns the referring URL for this request.
822: *
823: * @param string|array|null $default Default URL to use if HTTP_REFERER cannot be read from headers
824: * @param bool $local If true, restrict referring URLs to local server
825: * @return string Referring URL
826: */
827: public function referer($default = null, $local = false)
828: {
829: if (!$this->request) {
830: return Router::url($default, !$local);
831: }
832:
833: $referer = $this->request->referer($local);
834: if ($referer === '/' && $default && $default !== $referer) {
835: $url = Router::url($default, !$local);
836: $base = $this->request->getAttribute('base');
837: if ($local && $base && strpos($url, $base) === 0) {
838: $url = substr($url, strlen($base));
839: if ($url[0] !== '/') {
840: $url = '/' . $url;
841: }
842:
843: return $url;
844: }
845:
846: return $url;
847: }
848:
849: return $referer;
850: }
851:
852: /**
853: * Handles pagination of records in Table objects.
854: *
855: * Will load the referenced Table object, and have the PaginatorComponent
856: * paginate the query using the request date and settings defined in `$this->paginate`.
857: *
858: * This method will also make the PaginatorHelper available in the view.
859: *
860: * @param \Cake\ORM\Table|string|\Cake\ORM\Query|null $object Table to paginate
861: * (e.g: Table instance, 'TableName' or a Query object)
862: * @param array $settings The settings/configuration used for pagination.
863: * @return \Cake\ORM\ResultSet|\Cake\Datasource\ResultSetInterface Query results
864: * @link https://book.cakephp.org/3.0/en/controllers.html#paginating-a-model
865: * @throws \RuntimeException When no compatible table object can be found.
866: */
867: public function paginate($object = null, array $settings = [])
868: {
869: if (is_object($object)) {
870: $table = $object;
871: }
872:
873: if (is_string($object) || $object === null) {
874: $try = [$object, $this->modelClass];
875: foreach ($try as $tableName) {
876: if (empty($tableName)) {
877: continue;
878: }
879: $table = $this->loadModel($tableName);
880: break;
881: }
882: }
883:
884: $this->loadComponent('Paginator');
885: if (empty($table)) {
886: throw new RuntimeException('Unable to locate an object compatible with paginate.');
887: }
888: $settings += $this->paginate;
889:
890: return $this->Paginator->paginate($table, $settings);
891: }
892:
893: /**
894: * Method to check that an action is accessible from a URL.
895: *
896: * Override this method to change which controller methods can be reached.
897: * The default implementation disallows access to all methods defined on Cake\Controller\Controller,
898: * and allows all public methods on all subclasses of this class.
899: *
900: * @param string $action The action to check.
901: * @return bool Whether or not the method is accessible from a URL.
902: * @throws \ReflectionException
903: */
904: public function isAction($action)
905: {
906: $baseClass = new ReflectionClass('Cake\Controller\Controller');
907: if ($baseClass->hasMethod($action)) {
908: return false;
909: }
910: try {
911: $method = new ReflectionMethod($this, $action);
912: } catch (ReflectionException $e) {
913: return false;
914: }
915:
916: return $method->isPublic();
917: }
918:
919: /**
920: * Called before the controller action. You can use this method to configure and customize components
921: * or perform logic that needs to happen before each controller action.
922: *
923: * @param \Cake\Event\Event $event An Event instance
924: * @return \Cake\Http\Response|null
925: * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
926: */
927: public function beforeFilter(Event $event)
928: {
929: return null;
930: }
931:
932: /**
933: * Called after the controller action is run, but before the view is rendered. You can use this method
934: * to perform logic or set view variables that are required on every request.
935: *
936: * @param \Cake\Event\Event $event An Event instance
937: * @return \Cake\Http\Response|null
938: * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
939: */
940: public function beforeRender(Event $event)
941: {
942: return null;
943: }
944:
945: /**
946: * The beforeRedirect method is invoked when the controller's redirect method is called but before any
947: * further action.
948: *
949: * If the event is stopped the controller will not continue on to redirect the request.
950: * The $url and $status variables have same meaning as for the controller's method.
951: * You can set the event result to response instance or modify the redirect location
952: * using controller's response instance.
953: *
954: * @param \Cake\Event\Event $event An Event instance
955: * @param string|array $url A string or array-based URL pointing to another location within the app,
956: * or an absolute URL
957: * @param \Cake\Http\Response $response The response object.
958: * @return \Cake\Http\Response|null
959: * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
960: */
961: public function beforeRedirect(Event $event, $url, Response $response)
962: {
963: return null;
964: }
965:
966: /**
967: * Called after the controller action is run and rendered.
968: *
969: * @param \Cake\Event\Event $event An Event instance
970: * @return \Cake\Http\Response|null
971: * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
972: */
973: public function afterFilter(Event $event)
974: {
975: return null;
976: }
977: }
978: