Completed
Push — develop ( 6adcbb...6abf52 )
by
unknown
10:12
created

Organization   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 486
Duplicated Lines 3.7 %

Coupling/Cohesion

Components 4
Dependencies 13

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 67
c 2
b 0
f 1
lcom 4
cbo 13
dl 18
loc 486
rs 3.0612

39 Methods

Rating   Name   Duplication   Size   Complexity  
A setParent() 0 6 1
A getParent() 0 4 1
A getHiringOrganizations() 0 4 1
A isHiringOrganization() 0 4 1
A setExternalId() 0 5 1
A getExternalId() 0 4 1
A setHydrator() 0 4 1
A getHydrator() 0 4 1
A setOrganizationName() 0 9 2
A getOrganizationName() 0 4 1
A getName() 0 7 2
A getSearchableProperties() 0 4 1
A setKeywords() 0 3 1
A clearKeywords() 0 3 1
A getKeywords() 0 3 1
A getPermissions() 0 11 3
A setPermissions() 9 9 2
A getPermissionsResourceId() 0 4 1
D getPermissionsUserIds() 0 38 10
A setImage() 0 5 1
A getImage() 0 4 1
A setContact() 0 8 2
A getContact() 0 7 2
A isDraft() 0 4 1
A setIsDraft() 0 5 1
A getDescription() 0 4 1
A setDescription() 0 6 1
A setEmployees() 0 9 2
A getEmployees() 0 13 3
A getEmployee() 0 13 4
A isOwner() 0 4 1
A isEmployee() 0 4 2
A isAssociated() 0 4 2
A updatePermissions() 0 23 3
A setUser() 9 9 2
A getUser() 0 4 1
A getJobs() 0 4 1
A getTemplate() 0 7 2
A setTemplate() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Organization often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Organization, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * YAWIK
4
 *
5
 * @copyright (c) 2013-2015 Cross Solution (http://cross-solution.de)
6
 * @license   MIT
7
 */
8
9
namespace Organizations\Entity;
10
11
use Auth\Entity\UserInterface;
12
use Core\Entity\AbstractIdentifiableModificationDateAwareEntity as BaseEntity;
13
use Core\Entity\Collection\ArrayCollection;
14
use Doctrine\Common\Collections\Collection;
15
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
16
use Core\Repository\DoctrineMongoODM\Annotation as Cam;
17
use Core\Entity\Permissions;
18
use Core\Entity\PermissionsInterface;
19
use Core\Entity\EntityInterface;
20
use Zend\Stdlib\Hydrator\HydratorInterface;
21
use Core\Entity\Hydrator\EntityHydrator;
22
use Core\Entity\DraftableEntityInterface;
23
24
/**
25
 * The organization.
26
 *
27
 * @ODM\Document(collection="organizations", repositoryClass="Organizations\Repository\Organization")
28
 * @ODM\HasLifecycleCallbacks
29
 *
30
 * @todo write test
31
 * @author Mathias Weitz <[email protected]>
32
 * @author Mathias Gelhausen <[email protected]>
33
 */
34
class Organization extends BaseEntity implements OrganizationInterface, DraftableEntityInterface
35
{
36
37
    /**
38
     * Event name of post construct event.
39
     *
40
     * @var string
41
     */
42
    const postConstruct = 'postRepositoryConstruct';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected POSTCONSTRUCT).
Loading history...
43
44
    /**
45
     * externalId. Allows external applications to reference their primary key.
46
     *
47
     * @var string
48
     * @ODM\String
49
     * @ODM\Index
50
     */
51
    protected $externalId;
52
    
53
    /**
54
     * The actual name of the organization.
55
     *
56
     * @var \Organizations\Entity\OrganizationName
57
     * @ODM\ReferenceOne(targetDocument="\Organizations\Entity\OrganizationName", simple=true, cascade="persist")
58
     */
59
    protected $organizationName;
60
61
    /**
62
     * Assigned permissions.
63
     *
64
     * @var PermissionsInterface
65
     * @ODM\EmbedOne(targetDocument="\Core\Entity\Permissions")
66
     */
67
    protected $permissions;
68
    
69
    /**
70
     * primary logo of an organization
71
     *
72
     * @var \Organizations\Entity\OrganizationImage
73
     * @ODM\ReferenceOne(targetDocument="\Organizations\Entity\OrganizationImage", inversedBy="organization", simple=true, nullable="true", cascade={"all"})
74
     */
75
    protected $image;
76
77
    /**
78
     * Flag indicating draft state of this job.
79
     *
80
     * @var bool
81
     * @ODM\Boolean
82
     */
83
    protected $isDraft = false;
84
    
85
    /**
86
     * Organization contact data.
87
     *
88
     * @ODM\EmbedOne(targetDocument="\Organizations\Entity\OrganizationContact") */
89
    protected $contact;
90
91
    /**
92
     * The organizations' description.
93
     *
94
     * @var string
95
     * @ODM\String
96
     */
97
    protected $description;
98
99
    /**
100
     * The parent of this organization.
101
     *
102
     * @see setParent()
103
     * @var OrganizationInterface | null
104
     * @ODM\ReferenceOne(targetDocument="\Organizations\Entity\Organization", simple=true, nullable=true)
105
     * @since 0.18
106
     */
107
    protected $parent;
108
109
    /**
110
     * The hiring organizations of this organization.
111
     *
112
     * @var Collection
113
     * @ODM\ReferenceMany(
114
     *      targetDocument="Organizations\Entity\Organization",
115
     *      repositoryMethod="getHiringOrganizationsCursor"
116
     * )
117
     * @since 0.18
118
     */
119
    protected $hiringOrganizations;
120
121
    /**
122
     * The associated employees (users)
123
     *
124
     * @ODM\EmbedMany(targetDocument="\Organizations\Entity\Employee")
125
     * @var Collection
126
     */
127
    protected $employees;
128
129
    /**
130
     * Jobs of this organization.
131
     *
132
     * @var Collection
133
     * @ODM\ReferenceMany(targetDocument="\Jobs\Entity\Job", simple=true, mappedBy="organization")
134
     * @since 0.18
135
     */
136
    protected $jobs;
137
138
    /**
139
     * the owner of a Organization
140
     *
141
     * @var UserInterface $user
142
     * @ODM\ReferenceOne(targetDocument="\Auth\Entity\User", simple=true)
143
     * @ODM\Index
144
     */
145
    protected $user;
146
147
    /**
148
     * Default values of an organizations job template
149
     *
150
     * @var TemplateInterface;
151
     */
152
    protected $template;
153
154
    /**
155
     * @param OrganizationInterface $parent
156
     *
157
     * @return $this
158
     */
159
    public function setParent(OrganizationInterface $parent)
160
    {
161
        $this->parent = $parent;
162
163
        return $this;
164
    }
165
166
    public function getParent()
167
    {
168
        return $this->parent;
169
    }
170
171
    public function getHiringOrganizations()
172
    {
173
        return $this->hiringOrganizations;
174
    }
175
176
    public function isHiringOrganization()
177
    {
178
        return null !== $this->parent;
179
    }
180
181
    /**
182
     * Sets the external id.
183
     *
184
     * @todo Has to be in interface!
185
     * @param $externalId
186
     *
187
     * @return self
188
     */
189
    public function setExternalId($externalId)
190
    {
191
        $this->externalId = $externalId;
192
        return $this;
193
    }
194
195
    /**
196
     * Gets the internal id.
197
     * @todo has to be in interface!
198
     *
199
     * @return string
200
     */
201
    public function getExternalId()
202
    {
203
        return $this->externalId;
204
    }
205
206
    public function setHydrator(HydratorInterface $hydrator)
207
    {
208
        return $this;
209
    }
210
211
    public function getHydrator()
212
    {
213
        return new EntityHydrator();
214
    }
215
216
    public function setOrganizationName(OrganizationName $organizationName)
217
    {
218
        if (isset($this->organizationName)) {
219
            $this->organizationName->refCounterDec()->refCompanyCounterDec();
220
        }
221
        $this->organizationName = $organizationName;
222
        $this->organizationName->refCounterInc()->refCompanyCounterInc();
223
        return $this;
224
    }
225
226
    public function getOrganizationName()
227
    {
228
        return $this->organizationName;
229
    }
230
231
232
    public function getName()
233
    {
234
        if (empty($this->organizationName)) {
235
            return '';
236
        }
237
        return $this->organizationName->name;
0 ignored issues
show
Documentation introduced by
The property $name is declared protected in Organizations\Entity\OrganizationName. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
238
    }
239
240
    public function getSearchableProperties()
241
    {
242
        return array();
243
    }
244
245
    public function setKeywords(array $keywords)
246
    {
247
    }
248
249
    public function clearKeywords()
250
    {
251
    }
252
253
    public function getKeywords()
254
    {
255
    }
256
257
    public function getPermissions()
258
    {
259
        if (!$this->permissions) {
260
            $permissions = new Permissions();
261
            if ($this->user) {
262
                $permissions->grant($this->user, Permissions::PERMISSION_ALL);
0 ignored issues
show
Documentation introduced by
$this->user is of type object<Auth\Entity\UserInterface>, but the function expects a string|object<Core\Entit...sionsResourceInterface>.

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...
263
            }
264
            $this->setPermissions($permissions);
265
        }
266
        return $this->permissions;
267
    }
268
269 View Code Duplication
    public function setPermissions(PermissionsInterface $permissions)
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...
270
    {
271
        // Assure the user has always all rights.
272
        if ($this->user) {
273
            $permissions->grant($this->user, Permissions::PERMISSION_ALL);
274
        }
275
        $this->permissions = $permissions;
276
        return $this;
277
    }
278
279
    public function getPermissionsResourceId()
280
    {
281
        return 'organization:' . $this->getId();
282
    }
283
284
    public function getPermissionsUserIds($type = null)
285
    {
286
        // if we have a user, grant him full access to all associated permissions.
287
        $user = $this->getUser();
288
        $spec = $user
289
              ? $spec = array(PermissionsInterface::PERMISSION_ALL => array($this->getUser()->getId()))
290
              : array();
291
292
        if (null === $type || ('Job/Permissions' != $type && 'Application' != $type)) {
293
            return $spec;
294
        }
295
296
        if ('Job/Permissions' == $type) {
297
            $change = EmployeePermissionsInterface::JOBS_CHANGE;
298
            $view = EmployeePermissionsInterface::JOBS_VIEW;
299
        } else {
300
            $change = EmployeePermissionsInterface::APPLICATIONS_CHANGE;
301
            $view = EmployeePermissionsInterface::APPLICATIONS_VIEW;
302
        }
303
304
        $employees = $this->getEmployees();
305
306
        foreach ($employees as $emp) {
307
            /* @var $emp EmployeeInterface */
308
            if ($emp->isUnassigned()) {
309
                continue;
310
            }
311
312
            $perm = $emp->getPermissions();
313
            if ($perm->isAllowed($change)) {
314
                $spec[PermissionsInterface::PERMISSION_CHANGE][] = $emp->getUser()->getId();
315
            } elseif ($perm->isAllowed($view)) {
316
                $spec[PermissionsInterface::PERMISSION_VIEW][] = $emp->getUser()->getId();
317
            }
318
        }
319
320
        return $spec;
321
    }
322
323
    /**
324
     * Sets logo.
325
     *
326
     * @todo has to be in interface
327
     * @param OrganizationImage $image
0 ignored issues
show
Documentation introduced by
Should the type for parameter $image not be null|OrganizationImage?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
328
     *
329
     * @return self
330
     */
331
    public function setImage(OrganizationImage $image = null)
332
    {
333
        $this->image = $image;
334
        return $this;
335
    }
336
337
    /**
338
     * gets image
339
     *
340
     * @todo has to be in interface
341
     * @return OrganizationImage
342
     */
343
    public function getImage()
344
    {
345
        return $this->image;
346
    }
347
348
    public function setContact(EntityInterface $contact = null)
349
    {
350
        if (!$contact instanceof OrganizationContact) {
351
            $contact = new OrganizationContact($contact);
0 ignored issues
show
Unused Code introduced by
The call to OrganizationContact::__construct() has too many arguments starting with $contact.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
352
        }
353
        $this->contact = $contact;
354
        return $this;
355
    }
356
357
    public function getContact()
358
    {
359
        if (!$this->contact instanceof OrganizationContact) {
360
            $this->contact = new OrganizationContact();
361
        }
362
        return $this->contact;
363
    }
364
365
    public function isDraft()
366
    {
367
        return $this->isDraft;
368
    }
369
370
    public function setIsDraft($flag)
371
    {
372
        $this->isDraft = (bool) $flag;
373
        return $this;
374
    }
375
376
    public function getDescription()
377
    {
378
        return $this->description;
379
    }
380
381
    public function setDescription($description)
382
    {
383
        $this->description = $description;
384
385
        return $this;
386
    }
387
388
    public function setEmployees(Collection $employees)
389
    {
390
        /* todo: Throw exception or at least log incidents, where employees are added to "hiring orgs" */
391
        if (!$this->isHiringOrganization()) {
392
            $this->employees = $employees;
393
        }
394
395
        return $this;
396
    }
397
398
    public function getEmployees()
399
    {
400
        if ($this->isHiringOrganization()) {
401
            // Always return empty list, as we never have employees in this case.
402
            return new ArrayCollection();
403
        }
404
405
        if (!$this->employees) {
406
            $this->setEmployees(new ArrayCollection());
407
        }
408
409
        return $this->employees;
410
    }
411
412
    public function getEmployee($userOrId)
413
    {
414
        $employees = $this->getEmployees();
415
        $userId    = $userOrId instanceof \Auth\Entity\UserInterface ? $userOrId->getId() : $userOrId;
416
417
        foreach ($employees as $employee) {
418
            if ($employee->getUser()->getId() == $userId) {
419
                return $employee;
420
            }
421
        }
422
423
        return null;
424
    }
425
426
    public function isOwner(UserInterface $user)
427
    {
428
        return $this->getUser()->getId() == $user->getId();
429
    }
430
431
    public function isEmployee(UserInterface $user)
432
    {
433
        return $this->refs && in_array($user->getId(), $this->refs->getEmployeeIds());
0 ignored issues
show
Documentation introduced by
The property refs does not exist on object<Organizations\Entity\Organization>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
434
    }
435
436
    public function isAssociated(UserInterface $user)
437
    {
438
        return $this->isOwner($user) || $this->isEmployee($user);
439
    }
440
441
    /**
442
     * Updates the organizationsPermissions to allow all employees to view this organization.
443
     *
444
     * In case of a HiringOrganization Permissions are granted to all employees of the parent
445
     * organization.
446
     *
447
     * @ODM\PreUpdate
448
     * @ODM\PrePersist
449
     * @since 0.18
450
     */
451
    public function updatePermissions()
452
    {
453
        if ($this->isHiringOrganization()) {
454
            $organization = $this->getParent();
455
            $owner        = $organization->getUser();
456
457
            $this->setUser($owner);
458
        } else {
459
            $organization = $this;
460
        }
461
462
463
        /* @var $employees null | ArrayCollection | \Doctrine\ODM\MongoDB\PersistentCollection */
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
464
        $employees = $organization->getEmployees();
465
466
        $perms = $this->getPermissions();
467
468
        foreach ($employees as $emp) {
0 ignored issues
show
Bug introduced by
The expression $employees of type null is not traversable.
Loading history...
469
            /* @var $emp \Organizations\Entity\Employee */
470
            $perms->grant($emp->getUser(), PermissionsInterface::PERMISSION_CHANGE, false);
0 ignored issues
show
Unused Code introduced by
The call to PermissionsInterface::grant() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
471
        }
472
        $perms->build();
473
    }
474
475 View Code Duplication
    public function setUser(UserInterface $user)
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...
476
    {
477
        if ($this->user) {
478
            $this->getPermissions()->revoke($this->user, Permissions::PERMISSION_ALL, false);
0 ignored issues
show
Unused Code introduced by
The call to PermissionsInterface::revoke() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
479
        }
480
        $this->user = $user;
481
        $this->getPermissions()->grant($user, Permissions::PERMISSION_ALL);
482
        return $this;
483
    }
484
485
    public function getUser()
486
    {
487
        return $this->user;
488
    }
489
490
    public function getJobs()
491
    {
492
        return $this->jobs;
493
    }
494
495
    /**
496
     * Gets default values of an organizations job template
497
     *
498
     * @return TemplateInterface
499
     */
500
    public function getTemplate()
501
    {
502
        if (null === $this->template){
503
            $this->template = new Template();
504
        }
505
        return $this->template;
506
    }
507
508
    /**
509
     * Sets default values of an organizations job template
510
     *
511
     * @return self
0 ignored issues
show
Documentation introduced by
Should the return type not be Organization|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
512
     */
513
    public function setTemplate(TemplateInterface $template)
514
    {
515
        // TODO: Implement setTemplate() method.
516
    }
517
518
519
}
520