Completed
Push — master ( 324319...e9ec8f )
by Bram
06:01
created

Attendee::getCacheFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Attendee.php
4
 *
5
 * @author Bram de Leeuw
6
 * Date: 09/03/17
7
 */
8
9
namespace Broarm\EventTickets;
10
11
use ArrayList;
12
use BaconQrCode;
13
use BetterButtonCustomAction;
14
use CalendarEvent;
15
use Config;
16
use DataObject;
17
use Director;
18
use Dompdf\Dompdf;
19
use Email;
20
use FieldList;
21
use File;
22
use Folder;
23
use Image;
24
use ManyManyList;
25
use Member;
26
use ReadonlyField;
27
use SilverStripe\Omnipay\Exception\Exception;
28
use SSViewer;
29
use Tab;
30
use TabSet;
31
use ViewableData;
32
33
/**
34
 * Class Attendee
35
 *
36
 * @package Broarm\EventTickets
37
 *
38
 * @property string    Title
39
 * @property string    TicketCode
40
 * @property boolean   TicketReceiver
41
 * @property boolean   CheckedIn
42
 * @property FieldList SavableFields    Field to be set in AttendeesField
43
 *
44
 * @property int       TicketID
45
 * @property int       TicketQRCodeID
46
 * @property int       TicketFileID
47
 * @property int       ReservationID
48
 * @property int       EventID
49
 * @property int       MemberID
50
 *
51
 * @method Reservation Reservation()
52
 * @method Ticket Ticket()
53
 * @method Image TicketQRCode()
54
 * @method File TicketFile()
55
 * @method Member Member()
56
 * @method CalendarEvent|TicketExtension Event()
57
 * @method ManyManyList Fields()
58
 */
59
class Attendee extends DataObject
60
{
61
    /**
62
     * Set this to true when you want to have a QR code that opens the check in page and validates the code.
63
     * The validation is only done with proper authorisation so guest cannot check themselves in by mistake.
64
     * By default only the ticket number is translated to an QR code. (for use with USB QR scanners)
65
     *
66
     * @var bool
67
     */
68
    private static $qr_as_link = false;
69
70
    private static $default_fields = array(
71
        'FirstName' => array(
72
            'Title' => 'First name',
73
            'FieldType' => 'UserTextField',
74
            'Required' => true,
75
            'Editable' => false
76
        ),
77
        'Surname' => array(
78
            'Title' => 'Surname',
79
            'FieldType' => 'UserTextField',
80
            'Required' => true,
81
            'Editable' => false
82
        ),
83
        'Email' => array(
84
            'Title' => 'Email',
85
            'FieldType' => 'UserEmailField',
86
            'Required' => true,
87
            'Editable' => false
88
        )
89
    );
90
91
    private static $table_fields = array(
92
        'Title',
93
        'Email'
94
    );
95
96
    private static $db = array(
97
        'Title' => 'Varchar(255)',
98
        'TicketReceiver' => 'Boolean',
99
        'TicketCode' => 'Varchar(255)',
100
        'CheckedIn' => 'Boolean'
101
    );
102
103
    private static $default_sort = 'Created DESC';
104
105
    private static $indexes = array(
106
        'TicketCode' => 'unique("TicketCode")'
107
    );
108
109
    private static $has_one = array(
110
        'Reservation' => 'Broarm\EventTickets\Reservation',
111
        'Ticket' => 'Broarm\EventTickets\Ticket',
112
        'Event' => 'CalendarEvent',
113
        'Member' => 'Member',
114
        'TicketQRCode' => 'Image',
115
        'TicketFile' => 'File'
116
    );
117
118
    private static $many_many = array(
119
        'Fields' => 'Broarm\EventTickets\UserField'
120
    );
121
122
    private static $many_many_extraFields = array(
123
        'Fields' => array(
124
            'Value' => 'Varchar(255)'
125
        )
126
    );
127
128
    private static $summary_fields = array(
129
        'Title' => 'Name',
130
        'Ticket.Title' => 'Ticket',
131
        'TicketCode' => 'Ticket #',
132
        'CheckedIn.Nice' => 'Checked in',
133
    );
134
135
    /**
136
     * Actions usable on the cms detail view
137
     *
138
     * @var array
139
     */
140
    private static $better_buttons_actions = array(
141
        'sendTicket',
142
        'createTicketFile'
143
    );
144
145
    protected static $cachedFields = array();
146
147
    public function getCMSFields()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
148
    {
149
        $fields = new FieldList(new TabSet('Root', $mainTab = new Tab('Main')));
150
151
        $fields->addFieldsToTab('Root.Main', array(
152
            ReadonlyField::create('TicketCode', _t('Attendee.Ticket', 'Ticket')),
153
            ReadonlyField::create('MyCheckedIn', _t('Attendee.CheckedIn', 'Checked in'), $this->dbObject('CheckedIn')->Nice())
154
        ));
155
156
        foreach ($this->Fields() as $field) {
157
            $fieldType = $field->getFieldType();
158
            $fields->addFieldToTab(
159
                'Root.Main',
160
                $fieldType::create("{$field->Name}[$field->ID]", $field->Title, $field->getValue())
161
            );
162
        }
163
164
        if ($this->TicketFile()->exists()) {
165
            $fields->addFieldToTab('Root.Main', $reservationFileField = ReadonlyField::create(
166
                'ReservationFile',
167
                _t('Attendee.Reservation', 'Reservation'),
168
                "<a class='readonly' href='{$this->TicketFile()->Link()}' target='_blank'>Download reservation PDF</a>"
169
            ));
170
            $reservationFileField->dontEscape = true;
171
        }
172
173
        $this->extend('updateCMSFields', $fields);
174
        return $fields;
175
    }
176
177
    /**
178
     * Add utility actions to the attendee details view
179
     *
180
     * @return FieldList
181
     */
182
    public function getBetterButtonsActions()
183
    {
184
        /** @var FieldList $fields */
185
        $fields = parent::getBetterButtonsActions();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class DataObject as the method getBetterButtonsActions() does only exist in the following sub-classes of DataObject: Broarm\EventTickets\Attendee, Broarm\EventTickets\Reservation. 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...
186
        if ($this->TicketFile()->exists() && !empty($this->getEmail())) {
187
            $fields->push(BetterButtonCustomAction::create('sendTicket', _t('Attendee.SEND', 'Send the ticket')));
188
        }
189
190
        if (!empty($this->getName()) && !empty($this->getEmail())) {
191
            $fields->push(BetterButtonCustomAction::create('createTicketFile', _t('Attendee.CREATE_TICKET', 'Create the ticket')));
192
        }
193
194
        return $fields;
195
    }
196
197
    /**
198
     * Attendee constructor
199
     * If the fields don't exist yet add them
200
     * If they do populate the record with the set data
201
     *
202
     * @param null $record
203
     * @param bool $isSingleton
204
     * @param null $model
205
     */
206
    public function __construct($record = null, $isSingleton = false, $model = null)
207
    {
208
        parent::__construct($record, $isSingleton, $model);
209
        //if (($event = $this->Event()) && $event->exists() && !$this->Fields()->exists()) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
210
        //    $this->Fields()->addMany($event->Fields()->column());
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
211
        //}
212
        
213
        /* todo populate the records with the set UserFields
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
214
        if ($this->Fields()->exists()) {
215
            // Populate the record with the set field names
216
            foreach ($this->Fields() as $field) {
217
                if (!isset($this->record[$field->Name])) {
218
                    $this->record[$field->Name] = $field->Value;
219
                }
220
            }
221
        }*/
222
    }
223
224
    /**
225
     * Set the title and ticket code before writing
226
     */
227
    public function onBeforeWrite()
228
    {
229
        // Set the title of the attendee
230
        $this->Title = $this->getName();
231
232
        // Generate the ticket code
233
        if ($this->exists() && empty($this->TicketCode)) {
234
            $this->TicketCode = $this->generateTicketCode();
235
        }
236
237
        if (
238
            $this->getEmail()
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getEmail() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
239
            && $this->getName()
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getName() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
240
            && !$this->TicketFile()->exists()
241
            && !$this->TicketQRCode()->exists()
242
        ) {
243
            $this->createQRCode();
244
            $this->createTicketFile();
245
        }
246
247
        if ($fields = $this->Fields()) {
248
            foreach ($fields as $field) {
249
                if ($value = $this->{"$field->Name[$field->ID]"}) {
250
                    //$cache = self::getCacheFactory();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
251
                    //$cache->save(serialize($value), $this->getFieldCacheKey($field));
0 ignored issues
show
Unused Code Comprehensibility introduced by
78% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
252
                    $fields->add($field->ID, array('Value' => $value));
253
                }
254
            }
255
        }
256
257
        parent::onBeforeWrite();
258
    }
259
260
    public function onAfterWrite()
261
    {
262
        parent::onAfterWrite();
263
        if (($event = $this->Event()) && $event->exists() && !$this->Fields()->exists()) {
264
            $this->Fields()->addMany($event->Fields()->column());
265
        }
266
    }
267
268
    /**
269
     * Delete any stray files before deleting the object
270
     */
271
    public function onBeforeDelete()
272
    {
273
        // If an attendee is deleted from the guest list remove it's qr code
274
        // after deleting the code it's not validatable anymore, simply here for cleanup
275
        if ($this->TicketQRCode()->exists()) {
276
            $this->TicketQRCode()->delete();
277
        }
278
279
        // cleanup the ticket file
280
        if ($this->TicketFile()->exists()) {
281
            $this->TicketFile()->delete();
282
        }
283
284
        if ($this->Fields()->exists()) {
285
            $this->Fields()->removeAll();
286
        }
287
288
        parent::onBeforeDelete();
289
    }
290
291
    /**
292
     * Create the folder for the qr code and ticket file
293
     *
294
     * @return Folder|DataObject|null
295
     */
296
    public function fileFolder()
297
    {
298
        return Folder::find_or_make("/event-tickets/{$this->Event()->URLSegment}/{$this->TicketCode}/");
299
    }
300
301
    /**
302
     * Utility method for fetching the default field, FirstName, value
303
     *
304
     * @return string|null
305
     */
306
    public function getFirstName()
307
    {
308
        return self::getUserField('FirstName');
309
    }
310
311
    /**
312
     * Utility method for fetching the default field, Surname, value
313
     *
314
     * @return string|null
315
     */
316
    public function getSurname()
317
    {
318
        return self::getUserField('Surname');
319
    }
320
321
    /**
322
     * Utility method for fetching the default field, Email, value
323
     *
324
     * @return string|null
325
     */
326
    public function getEmail()
327
    {
328
        return self::getUserField('Email');
329
    }
330
331
    /**
332
     * Get the combined first and last nave for display on the ticket and attendee list
333
     *
334
     * @return string|null
335
     */
336
    public function getName()
337
    {
338
        $mainContact = $this->Reservation()->MainContact();
339
        if ($this->getSurname()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getSurname() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
340
            return trim("{$this->getFirstName()} {$this->getSurname()}");
341
        } elseif ($mainContact->exists() && $mainContact->getSurname()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mainContact->getSurname() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
342
            return _t('Attendee.GUEST_OF', 'Guest of {name}', null, array('name' => $mainContact->getName()));
0 ignored issues
show
Documentation introduced by
array('name' => $mainContact->getName()) is of type array<string,?,{"name":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
343
        } else {
344
            return null;
345
        }
346
    }
347
348
    /**
349
     * Get the user field and store it in a static cache
350
     * todo: add a cache that saves the field value on save and retrieves the values here, dumb, so empty fields don't trigger queries
351
     *
352
     * @param $field
353
     * @return mixed|null|string
354
     */
355
    public function getUserField($field)
356
    {
357
        //$cache = self::getCacheFactory();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
358
        //return unserialize($cache->load($this->getFieldCacheKey($field)));
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
359
360
        if (isset(self::$cachedFields[$this->ID][$field])) {
361
            return self::$cachedFields[$this->ID][$field];
362
        } elseif ($userField = $this->Fields()->find('Name', $field)) {
363
            return self::$cachedFields[$this->ID][$field] = (string)$userField->getField('Value');
364
        }
365
366
        return null;
367
    }
368
369
    protected static function getCacheFactory()
370
    {
371
        return \SS_Cache::factory('event_tickets_user_field');
372
    }
373
374
    protected function getFieldCacheKey($field)
375
    {
376
        return md5(serialize(array($this->ID, $field)));
377
    }
378
379
    /**
380
     * Get the table fields for this attendee
381
     *
382
     * @return ArrayList
383
     */
384
    public function getTableFields()
385
    {
386
        $fields = new ArrayList();
387
        foreach (self::config()->get('table_fields') as $field) {
388
            $data = new ViewableData();
389
            $data->Header = _t("Attendee.$field", $field);
390
            $data->Value = $this->{$field};
391
            $fields->add($data);
392
        }
393
        return $fields;
394
    }
395
396
    /**
397
     * Get the unnamespaced singular name for display in the CMS
398
     *
399
     * @return string
400
     */
401
    public function singular_name()
402
    {
403
        $name = explode('\\', parent::singular_name());
404
        return trim(end($name));
405
    }
406
407
    /**
408
     * Generate a unique ticket id
409
     * Serves as the base for the QR code and ticket file
410
     *
411
     * @return string
412
     */
413
    public function generateTicketCode()
414
    {
415
        return uniqid($this->ID);
416
    }
417
418
    /**
419
     * Create a QRCode for the attendee based on the Ticket code
420
     *
421
     * @return Image
422
     */
423
    public function createQRCode()
424
    {
425
        $folder = $this->fileFolder();
426
        $relativeFilePath = "/{$folder->Filename}{$this->TicketCode}.png";
427
        $absoluteFilePath = Director::baseFolder() . $relativeFilePath;
428
429
        if (!$image = Image::get()->find('Filename', $relativeFilePath)) {
430
            // Generate the QR code
431
            $renderer = new BaconQrCode\Renderer\Image\Png();
432
            $renderer->setHeight(256);
433
            $renderer->setWidth(256);
434
            $writer = new BaconQrCode\Writer($renderer);
435
            if (self::config()->get('qr_as_link')) {
436
                $writer->writeFile($this->getCheckInLink(), $absoluteFilePath);
437
            } else {
438
                $writer->writeFile($this->TicketCode, $absoluteFilePath);
439
            }
440
441
            // Store the image in an image object
442
            $image = Image::create();
443
            $image->ParentID = $folder->ID;
444
            $image->OwnerID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
445
            $image->Title = $this->TicketCode;
446
            $image->setFilename($relativeFilePath);
447
            $image->write();
448
449
            // Attach the QR code to the Attendee
450
            $this->TicketQRCodeID = $image->ID;
451
            $this->write();
452
        }
453
454
        return $image;
455
    }
456
457
    /**
458
     * Creates a printable ticket for the attendee
459
     *
460
     * @return File
461
     */
462
    public function createTicketFile()
463
    {
464
        // Find or make a folder
465
        $folder = $this->fileFolder();
466
        $relativeFilePath = "/{$folder->Filename}{$this->TicketCode}.pdf";
467
        $absoluteFilePath = Director::baseFolder() . $relativeFilePath;
468
469
        if (!$file = File::get()->find('Filename', $relativeFilePath)) {
470
            $file = File::create();
471
            $file->ParentID = $folder->ID;
472
            $file->OwnerID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
473
            $file->Title = $this->TicketCode;
474
            $file->setFilename($relativeFilePath);
475
            $file->write();
476
        }
477
478
        // Set the template and parse the data
479
        $template = new SSViewer('PrintableTicket');
480
        $html = $template->process($this->data());// getViewableData());
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
481
482
        // Create a DomPDF instance
483
        $domPDF = new Dompdf();
484
        $domPDF->loadHtml($html);
485
        $domPDF->setPaper('A4');
486
        $domPDF->getOptions()->setDpi(150);
487
        $domPDF->render();
488
489
        // Save the pdf stream as a file
490
        file_put_contents($absoluteFilePath, $domPDF->output());
491
492
        // Attach the ticket file to the Attendee
493
        $this->TicketFileID = $file->ID;
494
        $this->write();
495
496
        return $file;
497
    }
498
499
    /**
500
     * Send the attendee ticket
501
     */
502 View Code Duplication
    public function sendTicket()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
503
    {
504
        // Get the mail sender or fallback to the admin email
505
        if (empty($from = Reservation::config()->get('mail_sender'))) {
506
            $from = Config::inst()->get('Email', 'admin_email');
507
        }
508
509
        $email = new Email();
510
        $email->setSubject(_t(
511
            'AttendeeMail.TITLE',
512
            'Your ticket for {event}',
513
            null,
514
            array(
0 ignored issues
show
Documentation introduced by
array('event' => $this->Event()->Title) is of type array<string,?,{"event":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
515
                'event' => $this->Event()->Title
516
            )
517
        ));
518
        $email->setFrom($from);
519
        $email->setTo($this->getEmail());
520
        $email->setTemplate('AttendeeMail');
521
        $email->populateTemplate($this);
522
        $this->extend('updateTicketMail', $email);
523
        $email->send();
524
    }
525
526
    /**
527
     * Get the checkin link
528
     *
529
     * @return string
530
     */
531
    public function getCheckInLink()
532
    {
533
        return $this->Event()->AbsoluteLink("checkin/{$this->TicketCode}");
534
    }
535
536
    /**
537
     * Check the attendee out
538
     */
539
    public function checkIn()
540
    {
541
        $this->CheckedIn = true;
542
        $this->write();
543
    }
544
545
    public function canCheckOut()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
546
    {
547
        return CheckInValidator::config()->get('allow_checkout');
548
    }
549
550
    /**
551
     * Check the attendee in
552
     */
553
    public function checkOut()
554
    {
555
        if ($this->canCheckOut()) {
556
            $this->CheckedIn = false;
557
            $this->write();
558
        }
559
    }
560
561
    public function canView($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
562
    {
563
        return $this->Reservation()->canView($member);
564
    }
565
566
    public function canEdit($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
567
    {
568
        return $this->Reservation()->canEdit($member);
569
    }
570
571
    public function canDelete($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
572
    {
573
        return $this->Reservation()->canDelete($member);
574
    }
575
576
    public function canCreate($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
577
    {
578
        return $this->Reservation()->canCreate($member);
579
    }
580
}
581