Completed
Push — master ( 43a701...9aa864 )
by Thomas
13:49
created

RelationshipMethodNameGenerator   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 78
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 12
c 1
b 0
f 0
lcom 0
cbo 6
dl 0
loc 78
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A generateMethodName() 0 11 3
A generatePluralMethodName() 0 11 3
A generateReverseMethodName() 0 11 3
A generateReversePluralMethodName() 0 11 3
1
<?php
2
namespace keeko\tools\generator\name;
3
4
use keeko\tools\model\Relationship;
5
use keeko\framework\utils\NameUtils;
6
7
class RelationshipMethodNameGenerator extends AbstractNameGenerator {
8
9
	/**
10
	 * Returns the method name in studly case to prefix with add/get/set/remove
11
	 * and call it on a model class
12
	 *
13
	 * @param Relationship $relationship
14
	 * @return string
15
	 */
16
	public function generateMethodName(Relationship $relationship) {
17
		// for many-to-many where model and foreign are the same
18
		if ($relationship->getType() == Relationship::MANY_TO_MANY && $relationship->isReflexive()) {
19
			$lk = $relationship->getLocalKey();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class keeko\tools\model\Relationship as the method getLocalKey() does only exist in the following sub-classes of keeko\tools\model\Relationship: keeko\tools\model\ManyToManyRelationship. 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...
20
			$foreign = $relationship->getForeign();
21
			return $foreign->getPhpName() . 'RelatedBy' . $lk->getLocalColumn()->getPhpName();
22
		}
23
24
		// else
25
		return $relationship->getRelatedName();
26
	}
27
28
	/**
29
	 * Returns the plural method name in studly case to prefix with add/get/set/remove
30
	 * and call it on a model class
31
	 *
32
	 * @param Relationship $relationship
33
	 * @return string
34
	 */
35
	public function generatePluralMethodName(Relationship $relationship) {
36
		// for many-to-many where model and foreign are the same
37
		if ($relationship->getType() == Relationship::MANY_TO_MANY && $relationship->isReflexive()) {
38
			$lk = $relationship->getLocalKey();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class keeko\tools\model\Relationship as the method getLocalKey() does only exist in the following sub-classes of keeko\tools\model\Relationship: keeko\tools\model\ManyToManyRelationship. 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...
39
			$foreign = $relationship->getForeign();
40
			return NameUtils::pluralize($foreign->getPhpName()) . 'RelatedBy' . $lk->getLocalColumn()->getPhpName();
41
		}
42
43
		// else
44
		return $relationship->getRelatedPluralName();
45
	}
46
47
	/**
48
	 * Returns the reverse method name in studly case to prefix with add/get/set/remove
49
	 * and call it on a model class
50
	 *
51
	 * @param Relationship $relationship
52
	 * @return string
53
	 */
54
	public function generateReverseMethodName(Relationship $relationship) {
55
		// for many-to-many where model and foreign are the same
56
		if ($relationship->getType() == Relationship::MANY_TO_MANY && $relationship->isReflexive()) {
57
			$fk = $relationship->getForeignKey();
58
			$foreign = $relationship->getForeign();
59
			return $foreign->getPhpName() . 'RelatedBy' . $fk->getLocalColumn()->getPhpName();
60
		}
61
62
		// else
63
		return $relationship->getReverseRelatedName();
64
	}
65
66
	/**
67
	 * Returns the reverse plural method name in studly case to prefix with add/get/set/remove
68
	 * and call it on a model class
69
	 *
70
	 * @param Relationship $relationship
71
	 * @return string
72
	 */
73
	public function generateReversePluralMethodName(Relationship $relationship) {
74
		// for many-to-many where model and foreign are the same
75
		if ($relationship->getType() == Relationship::MANY_TO_MANY && $relationship->isReflexive()) {
76
			$fk = $relationship->getForeignKey();
77
			$foreign = $relationship->getForeign();
78
			return NameUtils::pluralize($foreign->getPhpName()) . 'RelatedBy' . $fk->getLocalColumn()->getPhpName();
79
		}
80
81
		// else
82
		return $relationship->getReverseRelatedPluralName();
83
	}
84
}