Completed
Push — develop ( 2a3366...be87d5 )
by John
03:07
created

View::displayPageLinks()   C

Complexity

Conditions 14
Paths 66

Size

Total Lines 77
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 77
rs 5.2115
cc 14
eloc 44
nc 66
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Alpha\View;
4
5
use Alpha\Util\Logging\Logger;
6
use Alpha\Util\Config\ConfigProvider;
7
use Alpha\Model\ActiveRecord;
8
use Alpha\Model\Type\DEnum;
9
use Alpha\Exception\IllegalArguementException;
10
use Alpha\Util\Service\ServiceFactory;
11
use Alpha\View\Renderer\RendererProviderInterface;
12
use ReflectionClass;
13
14
/**
15
 * The master rendering view class for the Alpha Framework.
16
 *
17
 * @since 1.0
18
 *
19
 * @author John Collins <[email protected]>
20
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
21
 * @copyright Copyright (c) 2017, John Collins (founder of Alpha Framework).
22
 * All rights reserved.
23
 *
24
 * <pre>
25
 * Redistribution and use in source and binary forms, with or
26
 * without modification, are permitted provided that the
27
 * following conditions are met:
28
 *
29
 * * Redistributions of source code must retain the above
30
 *   copyright notice, this list of conditions and the
31
 *   following disclaimer.
32
 * * Redistributions in binary form must reproduce the above
33
 *   copyright notice, this list of conditions and the
34
 *   following disclaimer in the documentation and/or other
35
 *   materials provided with the distribution.
36
 * * Neither the name of the Alpha Framework nor the names
37
 *   of its contributors may be used to endorse or promote
38
 *   products derived from this software without specific
39
 *   prior written permission.
40
 *
41
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
42
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
43
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
44
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
45
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
46
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
49
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
51
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
52
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
53
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54
 * </pre>
55
 */
56
class View
57
{
58
    /**
59
     * The business object that will be rendered.
60
     *
61
     * @var \Alpha\Model\ActiveRecord
62
     *
63
     * @since 1.0
64
     */
65
    protected $record;
66
67
    /**
68
     * The rendering provider that will be used to render the active record.
69
     *
70
     * @var \Alpha\View\Renderer\RendererProviderInterface
71
     *
72
     * @since 1.2
73
     */
74
    private static $provider;
75
76
    /**
77
     * Trace logger.
78
     *
79
     * @var Logger
80
     *
81
     * @since 1.0
82
     */
83
    private static $logger = null;
84
85
    /**
86
     * Constructor for the View.  As this is protected, use the View::getInstance method from a public scope.
87
     *
88
     * @param ActiveRecord $record           The main business object that this view is going to render
89
     * @param string       $acceptHeader Optionally pass the HTTP Accept header to select the correct renderer provider.
90
     *
91
     * @throws \Alpha\Exception\IllegalArguementException
92
     *
93
     * @since 1.0
94
     */
95
    protected function __construct($record, $acceptHeader = null)
96
    {
97
        self::$logger = new Logger('View');
98
        self::$logger->debug('>>__construct(Record=['.var_export($record, true).'], acceptHeader=['.$acceptHeader.'])');
99
100
        $config = ConfigProvider::getInstance();
101
102
        if ($record instanceof ActiveRecord) {
103
            $this->record = $record;
104
        } else {
105
            throw new IllegalArguementException('The record type provided ['.get_class($record).'] is not defined anywhere!');
106
        }
107
108
        self::setProvider($config->get('app.renderer.provider.name'), $acceptHeader);
109
        self::$provider->setRecord($this->record);
110
111
        self::$logger->debug('<<__construct');
112
    }
113
114
    /**
115
     * Static method which returns a View object or a custom child view for the Record specified
116
     * if one exists.
117
     *
118
     * @param ActiveRecord $record           The main business object that this view is going to render
119
     * @param bool         $returnParent Flag to enforce the return of this object instead of a child (defaults to false)
120
     * @param string       $acceptHeader Optionally pass the HTTP Accept header to select the correct renderer provider.
121
     *
122
     * @return View Returns a View object, or a child view object if one exists for this record
123
     *
124
     * @since 1.0
125
     */
126
    public static function getInstance($record, $returnParent = false, $acceptHeader = null)
127
    {
128
        if (self::$logger == null) {
129
            self::$logger = new Logger('View');
130
        }
131
        self::$logger->debug('>>getInstance(Record=['.var_export($record, true).'], returnParent=['.$returnParent.'], acceptHeader=['.$acceptHeader.'])');
132
133
        $class = new ReflectionClass($record);
134
        $childView = $class->getShortname();
135
        $childView = $childView.'View';
136
137
        // Check to see if a custom view exists for this record, and if it does return that view instead
138
        if (!$returnParent) {
139
            $className = '\Alpha\View\\'.$childView;
140
141
            if (class_exists($className)) {
142
                self::$logger->debug('<<getInstance [new '.$className.'('.get_class($record).')]');
143
144
                $instance = new $className($record, $acceptHeader);
145
146
                return $instance;
147
            }
148
149
            $className = '\View\\'.$childView;
150
151
            if (class_exists('\View\\'.$childView)) {
152
                self::$logger->debug('<<getInstance [new '.$className.'('.get_class($record).')]');
153
154
                $instance = new $className($record, $acceptHeader);
155
156
                return $instance;
157
            }
158
159
            self::$logger->debug('<<getInstance [new View('.get_class($record).', '.$acceptHeader.')]');
160
161
            return new self($record, $acceptHeader);
162
        } else {
163
            self::$logger->debug('<<getInstance [new View('.get_class($record).', '.$acceptHeader.')]');
164
165
            return new self($record, $acceptHeader);
166
        }
167
    }
168
169
    /**
170
     * Simple setter for the view business object.
171
     *
172
     * @param \Alpha\Model\ActiveRecord $record
173
     *
174
     * @throws \Alpha\Exception\IllegalArguementException
175
     *
176
     * @since 1.0
177
     */
178
    public function setRecord($record)
179
    {
180
        self::$logger->debug('>>setRecord(Record=['.var_export($record, true).'])');
181
182
        if ($record instanceof \Alpha\Model\ActiveRecord) {
183
            $this->record = $record;
184
        } else {
185
            throw new IllegalArguementException('The Record provided ['.get_class($record).'] is not defined anywhere!');
186
        }
187
188
        self::$logger->debug('<<setRecord');
189
    }
190
191
    /**
192
     * Gets the Record attached to this view (if any).
193
     *
194
     * @return \Alpha\Model\ActiveRecord
195
     *
196
     * @since 1.0
197
     */
198
    public function getRecord()
199
    {
200
        return $this->record;
201
    }
202
203
    /**
204
     * Renders the default create view.
205
     *
206
     * @param array $fields Hash array of fields to pass to the template
207
     *
208
     * @return string
209
     *
210
     * @since 1.0
211
     */
212
    public function createView($fields = array())
213
    {
214
        self::$logger->debug('>>createView(fields=['.var_export($fields, true).'])');
215
216
        if (method_exists($this, 'before_createView_callback')) {
217
            $this->{'before_createView_callback'}();
218
        }
219
220
        $body = self::$provider->createView($fields);
221
222
        if (method_exists($this, 'after_createView_callback')) {
223
            $this->{'after_createView_callback'}();
224
        }
225
226
        self::$logger->debug('<<createView');
227
228
        return $body;
229
    }
230
231
    /**
232
     * Renders a form to enable object editing.
233
     *
234
     * @param array $fields Hash array of fields to pass to the template
235
     *
236
     * @return string
237
     *
238
     * @since 1.0
239
     */
240
    public function editView($fields = array())
241
    {
242
        self::$logger->debug('>>editView(fields=['.var_export($fields, true).'])');
243
244
        if (method_exists($this, 'before_editView_callback')) {
245
            $this->{'before_editView_callback'}();
246
        }
247
248
        $body = self::$provider->editView($fields);
249
250
        if (method_exists($this, 'after_editView_callback')) {
251
            $this->{'after_editView_callback'}();
252
        }
253
254
        self::$logger->debug('<<editView');
255
256
        return $body;
257
    }
258
259
    /**
260
     * Renders the list view.
261
     *
262
     * @param array $fields Hash array of fields to pass to the template
263
     *
264
     * @return string
265
     *
266
     * @since 1.0
267
     */
268
    public function listView($fields = array())
269
    {
270
        self::$logger->debug('>>listView(fields=['.var_export($fields, true).'])');
271
272
        if (method_exists($this, 'before_listView_callback')) {
273
            $this->{'before_listView_callback'}();
274
        }
275
276
        $body = self::$provider->listView($fields);
277
278
        if (method_exists($this, 'after_listView_callback')) {
279
            $this->{'after_listView_callback'}();
280
        }
281
282
        self::$logger->debug('<<listView');
283
284
        return $body;
285
    }
286
287
    /**
288
     * Renders a detailed view of the object (read-only).
289
     *
290
     * @param array $fields Hash array of fields to pass to the template
291
     *
292
     * @return string
293
     *
294
     * @since 1.0
295
     */
296
    public function detailedView($fields = array())
297
    {
298
        self::$logger->debug('>>detailedView(fields=['.var_export($fields, true).'])');
299
300
        if (method_exists($this, 'before_detailedView_callback')) {
301
            $this->{'before_detailedView_callback'}();
302
        }
303
304
        $body = self::$provider->detailedView($fields);
305
306
        if (method_exists($this, 'after_detailedView_callback')) {
307
            $this->{'after_detailedView_callback'}();
308
        }
309
310
        self::$logger->debug('<<detailedView');
311
312
        return $body;
313
    }
314
315
    /**
316
     * Renders the admin view for the business object screen.
317
     *
318
     * @param array $fields Hash array of fields to pass to the template
319
     *
320
     * @return string
321
     *
322
     * @since 1.0
323
     */
324
    public function adminView($fields = array())
325
    {
326
        self::$logger->debug('>>adminView(fields=['.var_export($fields, true).'])');
327
328
        if (method_exists($this, 'before_adminView_callback')) {
329
            $this->{'before_adminView_callback'}();
330
        }
331
332
        $body = self::$provider->adminView($fields);
333
334
        if (method_exists($this, 'after_adminView_callback')) {
335
            $this->{'after_adminView_callback'}();
336
        }
337
338
        self::$logger->debug('<<adminView');
339
340
        return $body;
341
    }
342
343
    /**
344
     * Method to render the page header content.
345
     *
346
     * @param \Alpha\Controller\Controller $controller
347
     *
348
     * @return string
349
     *
350
     * @throws \Alpha\Exception\IllegalArguementException
351
     *
352
     * @since 1.0
353
     */
354
    public static function displayPageHead($controller)
355
    {
356
        if (self::$logger == null) {
357
            self::$logger = new Logger('View');
358
        }
359
        self::$logger->debug('>>displayPageHead(controller=['.var_export($controller, true).'])');
360
361
        if (method_exists($controller, 'before_displayPageHead_callback')) {
362
            $controller->{'before_displayPageHead_callback'}();
363
        }
364
365
        $config = ConfigProvider::getInstance();
366
367
        if (!self::$provider instanceof RendererProviderInterface) {
368
            self::setProvider($config->get('app.renderer.provider.name'));
369
        }
370
371
        $provider = self::$provider;
372
        $header = $provider::displayPageHead($controller);
373
374
        if (method_exists($controller, 'after_displayPageHead_callback')) {
375
            $header .= $controller->{'after_displayPageHead_callback'}();
376
        }
377
378
        self::$logger->debug('<<displayPageHead ['.$header.']');
379
380
        return $header;
381
    }
382
383
    /**
384
     * Method to render the page footer content.
385
     *
386
     * @param \Alpha\Controller\Controller $controller
387
     *
388
     * @return string
389
     *
390
     * @since 1.0
391
     */
392
    public static function displayPageFoot($controller)
393
    {
394
        if (self::$logger == null) {
395
            self::$logger = new Logger('View');
396
        }
397
398
        self::$logger->debug('>>displayPageFoot(controller=['.get_class($controller).'])');
399
400
        $config = ConfigProvider::getInstance();
401
402
        $footer = '';
403
404
        if (method_exists($controller, 'before_displayPageFoot_callback')) {
405
            $footer .= $controller->before_displayPageFoot_callback();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Alpha\Controller\Controller as the method before_displayPageFoot_callback() does only exist in the following sub-classes of Alpha\Controller\Controller: Alpha\Controller\ActiveRecordController, Alpha\Controller\ArticleController, Alpha\Controller\DEnumController, Alpha\Controller\LoginController, Alpha\Controller\SearchController, Alpha\Controller\SequenceController, Alpha\Controller\TagController. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
406
        }
407
408
        if (!self::$provider instanceof RendererProviderInterface) {
409
            self::setProvider($config->get('app.renderer.provider.name'));
410
        }
411
412
        $provider = self::$provider;
413
        $footer .= $provider::displayPageFoot($controller);
414
415
        if (method_exists($controller, 'after_displayPageFoot_callback')) {
416
            $footer .= $controller->{'after_displayPageFoot_callback'}();
417
        }
418
419
        self::$logger->debug('<<displayPageFoot ['.$footer.']');
420
421
        return $footer;
422
    }
423
424
    /**
425
     * Method for rendering the pagination links.
426
     *
427
     * @param \Alpha\Controller\Controller $controller
428
     *
429
     * @return string
430
     *
431
     * @since 3.0
432
     */
433
    public static function displayPageLinks($controller)
434
    {
435
        $config = ConfigProvider::getInstance();
436
437
        $html = '';
438
        $recordCount = $controller->getRecordCount();
0 ignored issues
show
Bug introduced by
The method getRecordCount() does not exist on Alpha\Controller\Controller. Did you maybe mean getRecord()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
439
        $start = $controller->getStart();
0 ignored issues
show
Bug introduced by
The method getStart() does not exist on Alpha\Controller\Controller. Did you maybe mean getStartTime()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
440
        $limit = $controller->getLimit();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Alpha\Controller\Controller as the method getLimit() does only exist in the following sub-classes of Alpha\Controller\Controller: Alpha\Controller\ActiveRecordController, Alpha\Controller\ArticleController, Alpha\Controller\DEnumController, Alpha\Controller\SequenceController, Alpha\Controller\TagController. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
441
442
        // the index of the last record displayed on this page
443
        $last = $start+$config->get('app.list.page.amount');
444
445
        // ensure that the last index never overruns the total record count
446
        if ($last > $recordCount) {
447
            $last = $recordCount ;
448
        }
449
450
        // render a message for an empty list
451
        if ($recordCount  > 0) {
452
            $html .= '<ul class="pagination">';
453
        } else {
454
            $html .= '<p align="center">The list is empty.&nbsp;&nbsp;</p>';
455
456
            return $html;
457
        }
458
459
        // render "Previous" link
460
        if ($start  > 0) {
461
            // handle secure URLs
462
            if ($controller->getRequest()->getParam('token', null) != null) {
463
                $url = FrontController::generateSecureURL('act=Alpha\Controller\ActiveRecordController&ActiveRecordType='.$controller->getRequest()->getParam('ActiveRecordType').'&start='.($start-$controller->getLimit()).'&limit='.$limit);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Alpha\Controller\Controller as the method getLimit() does only exist in the following sub-classes of Alpha\Controller\Controller: Alpha\Controller\ActiveRecordController, Alpha\Controller\ArticleController, Alpha\Controller\DEnumController, Alpha\Controller\SequenceController, Alpha\Controller\TagController. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
464
            } else {
465
                $url = '/records/'.urlencode($controller->getRequest()->getParam('ActiveRecordType')).'/'.($start-$limit).'/'.$limit;
466
            }
467
            $html .= '<li><a href="'.$url.'">&lt;&lt;-Previous</a></li>';
468
        } elseif ($recordCount  > $limit) {
469
            $html .= '<li class="disabled"><a href="#">&lt;&lt;-Previous</a></li>';
470
        }
471
472
        // render the page index links
473
        if ($recordCount  > $limit) {
474
            $page = 1;
475
476
            for ($i = 0; $i < $recordCount ; $i += $limit) {
477
                if ($i != $start) {
478
                    // handle secure URLs
479
                    if ($controller->getRequest()->getParam('token', null) != null) {
480
                        $url = FrontController::generateSecureURL('act=Alpha\Controller\ActiveRecordController&ActiveRecordType='.$controller->getRequest()->getParam('ActiveRecordType').'&start='.$i.'&limit='.$limit);
481
                    } else {
482
                        $url = '/records/'.urlencode($controller->getRequest()->getParam('ActiveRecordType')).'/'.$i.'/'.$limit;
483
                    }
484
                    $html .= '<li><a href="'.$url.'">'.$page.'</a></li>';
485
                } elseif ($recordCount  > $limit) { // render an anchor for the current page
486
                    $html .= '<li class="active"><a href="#">'.$page.'</a></li>';
487
                }
488
489
                ++$page;
490
            }
491
        }
492
493
        // render "Next" link
494
        if ($recordCount  > $last) {
495
            // handle secure URLs
496
            if ($controller->getRequest()->getParam('token', null) != null) {
497
                $url = FrontController::generateSecureURL('act=Alpha\Controller\ActiveRecordController&ActiveRecordType='.$controller->getRequest()->getParam('ActiveRecordType').'&start='.($start+$limit).'&limit='.$limit);
498
            } else {
499
                $url = '/records/'.urlencode($controller->getRequest()->getParam('ActiveRecordType')).'/'.($start+$limit.'/'.$limit);
500
            }
501
            $html .= '<li><a href="'.$url.'">Next-&gt;&gt;</a></li>';
502
        } elseif ($recordCount  > $limit) {
503
            $html .= '<li class="disabled"><a href="#">Next-&gt;&gt;</a></li>';
504
        }
505
506
        $html .= '</ul>';
507
508
        return $html;
509
    }
510
511
    /**
512
     * Renders the content for an update (e.g. successful save) message.
513
     *
514
     * @param string $message
515
     *
516
     * @return string
517
     *
518
     * @since 1.0
519
     */
520
    public static function displayUpdateMessage($message)
521
    {
522
        if (self::$logger == null) {
523
            self::$logger = new Logger('View');
524
        }
525
        self::$logger->debug('>>displayUpdateMessage(message=['.$message.'])');
526
527
        $config = ConfigProvider::getInstance();
528
529
        if (!self::$provider instanceof RendererProviderInterface) {
530
            self::setProvider($config->get('app.renderer.provider.name'));
531
        }
532
533
        $provider = self::$provider;
534
        $message = $provider::displayUpdateMessage($message);
535
536
        self::$logger->debug('<<displayUpdateMessage ['.$message.']');
537
538
        return $message;
539
    }
540
541
    /**
542
     * Renders the content for an error (e.g. save failed) message.
543
     *
544
     * @param string $message
545
     *
546
     * @return string
547
     *
548
     * @since 1.0
549
     */
550
    public static function displayErrorMessage($message)
551
    {
552
        if (self::$logger == null) {
553
            self::$logger = new Logger('View');
554
        }
555
        self::$logger->debug('>>displayErrorMessage(message=['.$message.'])');
556
557
        $config = ConfigProvider::getInstance();
558
559
        if (!self::$provider instanceof RendererProviderInterface) {
560
            self::setProvider($config->get('app.renderer.provider.name'));
561
        }
562
563
        $provider = self::$provider;
564
        $message = $provider::displayErrorMessage($message);
565
566
        self::$logger->debug('<<displayErrorMessage ['.$message.']');
567
568
        return $message;
569
    }
570
571
    /**
572
     * Renders an error page with the supplied error code (typlically a HTTP code) and a message.
573
     *
574
     * @param string $code
575
     * @param string $message
576
     *
577
     * @return string
578
     *
579
     * @since 1.0
580
     */
581
    public static function renderErrorPage($code, $message)
582
    {
583
        if (self::$logger == null) {
584
            self::$logger = new Logger('View');
585
        }
586
        self::$logger->debug('>>renderErrorPage(code=['.$code.'],message=['.$message.'])');
587
588
        $config = ConfigProvider::getInstance();
589
590
        if (!self::$provider instanceof RendererProviderInterface) {
591
            self::setProvider($config->get('app.renderer.provider.name'));
592
        }
593
594
        $provider = self::$provider;
595
        $message = $provider::renderErrorPage($code, $message);
596
597
        self::$logger->debug('<<renderErrorPage ['.$message.']');
598
599
        return $message;
600
    }
601
602
    /**
603
     * Method to render a hidden HTML form for posting the ID of an object to be deleted.
604
     *
605
     * @param string $URI The URI that the form will point to
606
     *
607
     * @return string
608
     *
609
     * @since 1.0
610
     */
611
    public static function renderDeleteForm($URI)
612
    {
613
        if (self::$logger == null) {
614
            self::$logger = new Logger('View');
615
        }
616
        self::$logger->debug('>>renderDeleteForm()');
617
618
        $config = ConfigProvider::getInstance();
619
620
        if (!self::$provider instanceof RendererProviderInterface) {
621
            self::setProvider($config->get('app.renderer.provider.name'));
622
        }
623
624
        $provider = self::$provider;
625
        $html = $provider::renderDeleteForm($URI);
626
627
        self::$logger->debug('<<renderDeleteForm ['.$html.']');
628
629
        return $html;
630
    }
631
632
    /**
633
     * Method to render a HTML form with two hidden, hashed (MD5) form fields to be used as
634
     * a check to ensure that a post to the controller is being sent from the same server
635
     * as hosting it.
636
     *
637
     * @return string
638
     *
639
     * @since 1.0
640
     */
641
    public static function renderSecurityFields()
642
    {
643
        if (self::$logger == null) {
644
            self::$logger = new Logger('View');
645
        }
646
        self::$logger->debug('>>renderSecurityFields()');
647
648
        $config = ConfigProvider::getInstance();
649
650
        if (!self::$provider instanceof RendererProviderInterface) {
651
            self::setProvider($config->get('app.renderer.provider.name'));
652
        }
653
654
        $provider = self::$provider;
655
        $html = $provider::renderSecurityFields();
656
657
        self::$logger->debug('<<renderSecurityFields ['.$html.']');
658
659
        return $html;
660
    }
661
662
    /**
663
     * Method to render the default Integer HTML.
664
     *
665
     * @param string $name      The field name
666
     * @param string $label     The label to apply to the field
667
     * @param string $mode      The field mode (create/edit/view)
668
     * @param string $value     The field value (optional)
669
     *
670
     * @return string
671
     *
672
     * @since 1.0
673
     */
674
    public function renderIntegerField($name, $label, $mode, $value = '')
675
    {
676
        self::$logger->debug('>>renderIntegerField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.']');
677
678
        $html = self::$provider->renderIntegerField($name, $label, $mode, $value);
679
680
        self::$logger->debug('<<renderIntegerField ['.$html.']');
681
682
        return $html;
683
    }
684
685
    /**
686
     * Method to render the default Double HTML.
687
     *
688
     * @param string $name      The field name
689
     * @param string $label     The label to apply to the field
690
     * @param string $mode      The field mode (create/edit/view)
691
     * @param string $value     The field value (optional)
692
     *
693
     * @return string
694
     *
695
     * @since 1.0
696
     */
697
    public function renderDoubleField($name, $label, $mode, $value = '')
698
    {
699
        self::$logger->debug('>>renderDoubleField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.'])');
700
701
        $html = self::$provider->renderDoubleField($name, $label, $mode, $value);
702
703
        self::$logger->debug('<<renderDoubleField ['.$html.']');
704
705
        return $html;
706
    }
707
708
    /**
709
     * Method to render the default Boolean HTML.
710
     *
711
     * @param string $name      The field name
712
     * @param string $label     The label to apply to the field
713
     * @param string $mode      The field mode (create/edit/view)
714
     * @param string $value     The field value (optional)
715
     *
716
     * @return string
717
     *
718
     * @since 1.0
719
     */
720
    public function renderBooleanField($name, $label, $mode, $value = '')
721
    {
722
        self::$logger->debug('>>renderBooleanField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.'])');
723
724
        $html = self::$provider->renderBooleanField($name, $label, $mode, $value);
725
726
        self::$logger->debug('<<renderBooleanField ['.$html.']');
727
728
        return $html;
729
    }
730
731
    /**
732
     * Method to render the default Enum HTML.
733
     *
734
     * @param string $name      The field name
735
     * @param string $label     The label to apply to the field
736
     * @param string $mode      The field mode (create/edit/view)
737
     * @param array  $options   The Enum options
738
     * @param string $value     The field value (optional)
739
     *
740
     * @return string
741
     *
742
     * @since 1.0
743
     */
744
    public function renderEnumField($name, $label, $mode, $options, $value = '')
745
    {
746
        self::$logger->debug('>>renderEnumField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.'])');
747
748
        $html = self::$provider->renderEnumField($name, $label, $mode, $options, $value);
749
750
        self::$logger->debug('<<renderEnumField ['.$html.']');
751
752
        return $html;
753
    }
754
755
    /**
756
     * Method to render the default DEnum HTML.
757
     *
758
     * @param string $name      The field name
759
     * @param string $label     The label to apply to the field
760
     * @param string $mode      The field mode (create/edit/view)
761
     * @param array  $options   The DEnum options
762
     * @param string $value     The field value (optional)
763
     *
764
     * @return string
765
     *
766
     * @since 1.0
767
     */
768
    public function renderDEnumField($name, $label, $mode, $options, $value = '')
769
    {
770
        self::$logger->debug('>>renderDEnumField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.'])');
771
772
        $html = self::$provider->renderDEnumField($name, $label, $mode, $options, $value);
773
774
        self::$logger->debug('<<renderDEnumField ['.$html.']');
775
776
        return $html;
777
    }
778
779
    /**
780
     * Method to render the default field HTML when type is not known.
781
     *
782
     * @param string $name      The field name
783
     * @param string $label     The label to apply to the field
784
     * @param string $mode      The field mode (create/edit/view)
785
     * @param string $value     The field value (optional)
786
     *
787
     * @return string
788
     *
789
     * @since 1.0
790
     */
791
    public function renderDefaultField($name, $label, $mode, $value = '')
792
    {
793
        self::$logger->debug('>>renderDefaultField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.'])');
794
795
        $html = self::$provider->renderDefaultField($name, $label, $mode, $value);
796
797
        self::$logger->debug('<<renderDefaultField ['.$html.']');
798
799
        return $html;
800
    }
801
802
    /**
803
     * render the default Text HTML.
804
     *
805
     * @param string $name      The field name
806
     * @param string $label     The label to apply to the field
807
     * @param string $mode      The field mode (create/edit/view)
808
     * @param string $value     The field value (optional)
809
     *
810
     * @return string
811
     *
812
     * @since 1.0
813
     */
814
    public function renderTextField($name, $label, $mode, $value = '')
815
    {
816
        self::$logger->debug('>>renderTextField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.'])');
817
818
        $html = self::$provider->renderTextField($name, $label, $mode, $value);
819
820
        self::$logger->debug('<<renderTextField ['.$html.']');
821
822
        return $html;
823
    }
824
825
    /**
826
     * render the default Relation HTML.
827
     *
828
     * @param string $name      The field name
829
     * @param string $label     The label to apply to the field
830
     * @param string $mode      The field mode (create/edit/view)
831
     * @param string $value     The field value (optional)
832
     * @param bool   $expanded  Render the related fields in expanded format or not (optional)
833
     * @param bool   $buttons   Render buttons for expanding/contacting the related fields (optional)
834
     *
835
     * @return string
836
     *
837
     * @since 1.0
838
     */
839
    public function renderRelationField($name, $label, $mode, $value = '', $expanded = false, $buttons = true)
840
    {
841
        self::$logger->debug('>>renderRelationField(name=['.$name.'], label=['.$label.'], mode=['.$mode.'], value=['.$value.'], expanded=['.$expanded.'], buttons=['.$buttons.'])');
842
843
        $html = self::$provider->renderRelationField($name, $label, $mode, $value, $expanded, $buttons);
844
845
        self::$logger->debug('<<renderRelationField ['.$html.']');
846
847
        return $html;
848
    }
849
850
    /**
851
     * Renders all fields for the current Record in edit/create/view mode.
852
     *
853
     * @param string $mode           (view|edit|create)
854
     * @param array  $filterFields   Optional list of field names to exclude from rendering
855
     * @param array  $readOnlyFields Optional list of fields to render in a readonly fashion when rendering in create or edit mode
856
     *
857
     * @return string
858
     *
859
     * @since 1.0
860
     */
861
    public function renderAllFields($mode, $filterFields = array(), $readOnlyFields = array())
862
    {
863
        self::$logger->debug('>>renderAllFields(mode=['.$mode.'], filterFields=['.var_export($filterFields, true).'], readOnlyFields=['.var_export($readOnlyFields, true).'])');
864
865
        $html = self::$provider->renderAllFields($mode, $filterFields, $readOnlyFields);
866
867
        self::$logger->debug('<<renderAllFields ['.$html.']');
868
869
        return $html;
870
    }
871
872
    /**
873
     * Loads a template for the Record specified if one exists.  Lower level custom templates
874
     * take precedence.
875
     *
876
     * @param \Alpha\Model\ActiveRecord $record
877
     * @param string                   $mode
878
     * @param array                    $fields
879
     *
880
     * @return string
881
     *
882
     * @since 1.0
883
     *
884
     * @throws \Alpha\Exception\IllegalArguementException
885
     */
886
    public static function loadTemplate($record, $mode, $fields = array())
887
    {
888
        self::$logger->debug('>>loadTemplate(Record=['.var_export($record, true).'], mode=['.$mode.'], fields=['.var_export($fields, true).'])');
889
890
        $config = ConfigProvider::getInstance();
891
892
        // for each Record property, create a local variable holding its value
893
        $reflection = new ReflectionClass(get_class($record));
894
        $properties = $reflection->getProperties();
895
896
        foreach ($properties as $propObj) {
897
            $propName = $propObj->name;
898
899
            if ($propName != 'logger' && !$propObj->isPrivate()) {
900
                $prop = $record->getPropObject($propName);
901
                if ($prop instanceof DEnum) {
902
                    ${$propName} = $prop->getDisplayValue();
903
                } else {
904
                    ${$propName} = $record->get($propName);
905
                }
906
            }
907
        }
908
909
        // loop over the $fields array and create a local variable for each key value
910
        foreach (array_keys($fields) as $fieldName) {
911
            ${$fieldName} = $fields[$fieldName];
912
        }
913
914
        $filename = $mode.'.phtml';
915
        $class = new ReflectionClass($record);
916
        $className = $class->getShortname();
917
918
        $customPath = $config->get('app.root').'src/View/Html/Templates/'.$className.'/'.$filename;
919
        $defaultPath1 = $config->get('app.root').'vendor/alphadevx/alpha/Alpha/View/Renderer/Html/Templates/'.$className.'/'.$filename;
920
        $defaultPath2 = $config->get('app.root').'vendor/alphadevx/alpha/Alpha/View/Renderer/Html/Templates/'.$filename;
921
        $defaultPath3 = $config->get('app.root').'Alpha/View/Renderer/Html/Templates/'.$className.'/'.$filename;
922
        $defaultPath4 = $config->get('app.root').'Alpha/View/Renderer/Html/Templates/'.$filename;
923
924
        // Check to see if a custom template exists for this record, and if it does load that
925
        if (file_exists($customPath)) {
926
            self::$logger->debug('Loading template ['.$customPath.']');
927
            ob_start();
928
            require $customPath;
929
            $html = ob_get_clean();
930
931
            self::$logger->debug('<<loadTemplate');
932
            return $html;
933
        } elseif (file_exists($defaultPath1)) {
934
            self::$logger->debug('Loading template ['.$defaultPath1.']');
935
            ob_start();
936
            require $defaultPath1;
937
            $html = ob_get_clean();
938
939
            self::$logger->debug('<<loadTemplate');
940
            return $html;
941
        } elseif (file_exists($defaultPath2)) {
942
            self::$logger->debug('Loading template ['.$defaultPath2.']');
943
            ob_start();
944
            require $defaultPath2;
945
            $html = ob_get_clean();
946
947
            self::$logger->debug('<<loadTemplate');
948
            return $html;
949
        } elseif (file_exists($defaultPath3)) {
950
            self::$logger->debug('Loading template ['.$defaultPath3.']');
951
            ob_start();
952
            require $defaultPath3;
953
            $html = ob_get_clean();
954
955
            self::$logger->debug('<<loadTemplate');
956
            return $html;
957
        } elseif (file_exists($defaultPath4)) {
958
            self::$logger->debug('Loading template ['.$defaultPath4.']');
959
            ob_start();
960
            require $defaultPath4;
961
            $html = ob_get_clean();
962
963
            self::$logger->debug('<<loadTemplate');
964
            return $html;
965
        } else {
966
            self::$logger->debug('<<loadTemplate');
967
            throw new IllegalArguementException('No ['.$mode.'] HTML template found for class ['.$className.']');
968
        }
969
    }
970
971
    /**
972
     * Loads a template fragment from the Renderer/[type]/Fragments/[filename.ext] location.
973
     *
974
     * @param string $type     Currently only html supported, later json and xml.
975
     * @param string $fileName The name of the fragment file
976
     * @param array  $fields   A hash array of field values to pass to the template fragment.
977
     *
978
     * @return string
979
     *
980
     * @since 1.2
981
     *
982
     * @throws \Alpha\Exception\IllegalArguementException
983
     */
984
    public static function loadTemplateFragment($type, $fileName, $fields = array())
985
    {
986
        if (self::$logger == null) {
987
            self::$logger = new Logger('View');
988
        }
989
        self::$logger->debug('>>loadTemplateFragment(type=['.$type.'], fileName=['.$fileName.'], fields=['.var_export($fields, true).'])');
990
991
        $config = ConfigProvider::getInstance();
992
993
        // loop over the $fields array and create a local variable for each key value
994
        foreach (array_keys($fields) as $fieldName) {
995
            ${$fieldName} = $fields[$fieldName];
996
        }
997
998
        $customPath = $config->get('app.root').'src/View/'.ucfirst($type).'/Fragments/'.$fileName;
999
        $defaultPath1 = $config->get('app.root').'vendor/alphadevx/alpha/Alpha/View/Renderer/'.ucfirst($type).'/Fragments/'.$fileName;
1000
        $defaultPath2 = $config->get('app.root').'Alpha/View/Renderer/'.ucfirst($type).'/Fragments/'.$fileName;
1001
1002
        // Check to see if a custom template exists for this record, and if it does load that
1003
        if (file_exists($customPath)) {
1004
            self::$logger->debug('Loading template ['.$customPath.']');
1005
            ob_start();
1006
            require $customPath;
1007
            $html = ob_get_clean();
1008
1009
            self::$logger->debug('<<loadTemplateFragment');
1010
            return $html;
1011
        } elseif (file_exists($defaultPath1)) {
1012
            self::$logger->debug('Loading template ['.$defaultPath1.']');
1013
            ob_start();
1014
            require $defaultPath1;
1015
            $html = ob_get_clean();
1016
1017
            self::$logger->debug('<<loadTemplateFragment');
1018
            return $html;
1019
        } elseif (file_exists($defaultPath2)) {
1020
            self::$logger->debug('Loading template ['.$defaultPath2.']');
1021
            ob_start();
1022
            require $defaultPath2;
1023
            $html = ob_get_clean();
1024
1025
            self::$logger->debug('<<loadTemplateFragment');
1026
            return $html;
1027
        } else {
1028
            self::$logger->debug('<<loadTemplateFragment');
1029
            throw new IllegalArguementException('Template fragment not found in ['.$customPath.'] or ['.$defaultPath1.'] or ['.$defaultPath2.']!');
1030
        }
1031
    }
1032
1033
    /**
1034
     * Enables you to set an explicit type of RendererProviderInterface implementation to use for rendering the records
1035
     * attached to this view.
1036
     *
1037
     * @param string $ProviderClassName The name of the RendererProviderInterface implementation to use in this view object
1038
     * @param string $acceptHeader      Optional pass the HTTP Accept header to select the correct renderer provider.
1039
     *
1040
     * @since 1.2
1041
     *
1042
     * @throws \Alpha\Exception\IllegalArguementException
1043
     */
1044
    public static function setProvider($ProviderClassName, $acceptHeader = null)
1045
    {
1046
        if ($ProviderClassName == 'auto') {
1047
            $ProviderClassName = 'Alpha\View\Renderer\Html\RendererProviderHTML';
1048
1049
            if ($acceptHeader == 'application/json') {
1050
                $ProviderClassName = 'Alpha\View\Renderer\Json\RendererProviderJSON';
1051
            }
1052
1053
            self::$provider = ServiceFactory::getInstance($ProviderClassName, 'Alpha\View\Renderer\RendererProviderInterface');
1054
        } else {
1055
            if (class_exists($ProviderClassName)) {
1056
                $provider = new $ProviderClassName();
1057
1058
                if ($provider instanceof RendererProviderInterface) {
1059
                    self::$provider = ServiceFactory::getInstance($ProviderClassName, 'Alpha\View\Renderer\RendererProviderInterface');
1060
                } else {
1061
                    throw new IllegalArguementException('The provider class ['.$ProviderClassName.'] does not implement the RendererProviderInterface interface!');
1062
                }
1063
            } else {
1064
                throw new IllegalArguementException('The provider class ['.$ProviderClassName.'] does not exist!');
1065
            }
1066
        }
1067
    }
1068
1069
    /**
1070
     * Get the current view renderer provider.
1071
     *
1072
     * @return \Alpha\View\Renderer\RendererProviderInterface
1073
     *
1074
     * @since 2.0
1075
     */
1076
    public static function getProvider()
1077
    {
1078
        if (self::$provider instanceof RendererProviderInterface) {
1079
            return self::$provider;
1080
        } else {
1081
            $config = ConfigProvider::getInstance();
1082
1083
            self::$provider = ServiceFactory::getInstance($config->get('app.renderer.provider.name'), 'Alpha\View\Renderer\RendererProviderInterface');
1084
1085
            return self::$provider;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return self::$provider; (stdClass) is incompatible with the return type documented by Alpha\View\View::getProvider of type Alpha\View\Renderer\RendererProviderInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1086
        }
1087
    }
1088
}
1089