Completed
Push — master ( 2497fd...24ae5a )
by Stefano
02:23
created

Relation   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 0
loc 110
rs 10
c 0
b 0
f 0
wmc 22
lcom 1
cbo 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A relationOptions() 0 5 2
C relationAddRelationshipTo() 0 59 13
A __get() 0 5 2
A __set() 0 5 2
A __isset() 0 4 1
A hasOne() 0 3 1
1
<?php
2
3
/**
4
 * Relation trait
5
 *
6
 * Define a relation between two models.
7
 *
8
 * @package core
9
 * @author [email protected]
10
 * @copyright Caffeina srl - 2016 - http://caffeina.com
11
 */
12
13
trait Relation {
14
15
  /**
16
   * [Internal] : Retrieve/Set relation options
17
   * This function can be used to get all options passing null, setting options passing an associative
18
   * array or retrieve a single value passing a string
19
   *
20
   * @param  mixed $options The options passed to the relation layer.
0 ignored issues
show
Bug introduced by
There is no parameter named $options. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
21
   * @return mixed          All options array or a single value
22
   */
23
  protected static function & relationOptions(){
24
    static $_options;
25
    if ($_options === null) $_options = (object)[ 'links' => [], 'relations' => [] ];
26
    return $_options;
27
  }
28
29
  /**
30
   * [Internal] : Assigns or retrieve the Save callback
31
   * The save callback interface is
32
   *   function($table, array $options)
33
   *
34
   * @param  callable $callback The callback to use on model save
0 ignored issues
show
Bug introduced by
There is no parameter named $callback. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
35
   * @return callable           Current save callback
36
   */
37
  private static function relationAddRelationshipTo($link, $plurality, $extra=[]){
38
    $options = static::relationOptions();
39
40
    preg_match('((?<FOREIGN_CLASS>\w+)(\.(?<FOREIGN_KEY>\w+))?(:(?<LOCAL_KEY>\w+))?)', $link, $parts);
41
42
    $foreign_class = isset($parts['FOREIGN_CLASS']) ? $parts['FOREIGN_CLASS'] : false;
43
    $foreign_key   = isset($parts['FOREIGN_KEY'])   ? $parts['FOREIGN_KEY']   : false;
44
    $local_key     = isset($parts['LOCAL_KEY'])     ? $parts['LOCAL_KEY']     : false;
45
46
    if ( ! $foreign_class )
0 ignored issues
show
Bug Best Practice introduced by
The expression $foreign_class of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false 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...
47
       throw new Exception("[Core.Relation] Class ".get_called_class()." must define a foreign Model when assigning a relation.", 1);
48
49
    if ( ! is_subclass_of($foreign_class,"Model") )
50
       throw new Exception("[Core.Relation] Class ".get_called_class()." want to relate to $foreign_class but it's not a Model.", 1);
51
52
    if ( ! $foreign_key ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $foreign_key of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false 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...
53
      // Retrieve primary key from foreign class
54
      $foreign_key = $foreign_class::persistenceOptions("key");
55
    }
56
57
    if ( ! $local_key ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $local_key of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false 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...
58
      // Retrieve local primary key
59
      $local_key = static::persistenceOptions("key");
60
    }
61
62
    $single = $plurality == 'single';
63
64
    $method = preg_replace_callback('([A-Z])', function($m){
65
      return "_" . strtolower($m[0]);
66
    }, lcfirst($foreign_class) . ($single ? '' : 's'));
67
68
    $hh = [$foreign_class,$foreign_key,$local_key];
69
    sort($hh);
70
    $options->links[md5(serialize($hh))] = $rel = (object)[
71
      'foreign_class' =>  $foreign_class,
72
      'foreign_key'   =>  $foreign_key,
73
      'local_key'     =>  $local_key,
74
      'single'        =>  $single,
75
      'method'        =>  $method,
76
      'extra'         =>  (object) $extra,
77
    ];
78
79
    if (empty($options->relations)) $options->relations = (object)[];
80
    $options->relations->$method = $getset = (object)[
0 ignored issues
show
Unused Code introduced by
$getset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
81
      'get' => function($self) use ($foreign_class, $rel) {
82
         $val = $self->{$rel->local_key};
83
         $val = is_numeric($val) ? $val : "'" . addslashes($val) . "'";
84
         $data = $foreign_class::where("{$rel->foreign_key} = {$val}");
85
         return $rel->single ? current($data) : $data;
86
      },
87
      'set' => function($value, $self) use ($foreign_class, $rel) {
88
        if (!is_a($value, $foreign_class))
89
          throw new Exception("[Core.Relation] Relationship for {$rel->method} must be of class $foreign_class.", 1);
90
        $self->local_key = $value->foreign_key;
91
        return $value;
92
      },
93
    ];
94
95
  }
96
97
  public function __get($name){
98
    $options = static::relationOptions();
99
    if (isset($options->relations->$name))
100
      return call_user_func($options->relations->$name->get, $this);
101
  }
102
103
  public function __set($name, $value){
104
    $options = static::relationOptions();
105
    if (isset($options->relations->$name))
106
      call_user_func($options->relations->$name->set, $value, $this);
107
  }
108
109
  public function __isset($name){
110
    $options = static::relationOptions();
111
    return isset($options->relations->$name);
112
  }
113
114
  public static function hasOne($modelName, $extra=[]){
115
    return static::relationAddRelationshipTo($modelName, 'single', $extra);
116
  }
117
118
  public static function hasMany($modelName, $extra=[]){
119
    return static::relationAddRelationshipTo($modelName, 'multiple', $extra);
120
  }
121
122
}
123