Completed
Push — 3.7 ( 11b87a...bb5701 )
by
unknown
09:09 queued 01:14
created

PolymorphicForeignKey::isChanged()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * A special ForeignKey class that handles relations with arbitrary class types
5
 *
6
 * @package framework
7
 * @subpackage model
8
 */
9
class PolymorphicForeignKey extends ForeignKey implements CompositeDBField {
10
11
	/**
12
	 * @var boolean $isChanged
13
	 */
14
	protected $isChanged = false;
15
16
	/**
17
	 * Value of relation class
18
	 *
19
	 * @var string
20
	 */
21
	protected $classValue = null;
22
23
	/**
24
	 * Field definition cache for compositeDatabaseFields
25
	 *
26
	 * @var string
27
	 */
28
	protected static $classname_spec_cache = array();
29
30
	/**
31
	 * Clear all cached classname specs. It's necessary to clear all cached subclassed names
32
	 * for any classes if a new class manifest is generated.
33
	 */
34
	public static function clear_classname_spec_cache() {
35
		self::$classname_spec_cache = array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $classname_spec_cache.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
36
	}
37
38
	public function scaffoldFormField($title = null, $params = null) {
39
		// Opt-out of form field generation - Scaffolding should be performed on
40
		// the has_many end, or set programatically.
41
		// @todo - Investigate suitable FormField
42
		return null;
43
	}
44
45
	public function requireField() {
46
		$fields = $this->compositeDatabaseFields();
47
		if($fields) foreach($fields as $name => $type){
0 ignored issues
show
Bug Best Practice introduced by
The expression $fields of type array<string,string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
48
			DB::requireField($this->tableName, $this->name.$name, $type);
0 ignored issues
show
Deprecated Code introduced by
The method DB::requireField() has been deprecated with message: since version 4.0 Use DB::require_field instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
49
		}
50
	}
51
52
	public function writeToManipulation(&$manipulation) {
53
54
		// Write ID, checking that the value is valid
55
		$manipulation['fields'][$this->name . 'ID'] = $this->exists()
56
			? $this->prepValueForDB($this->getIDValue())
57
			: $this->nullValue();
58
59
		// Write class
60
		$classObject = DBField::create_field('Enum', $this->getClassValue(), $this->name . 'Class');
61
		$classObject->writeToManipulation($manipulation);
62
	}
63
64
	public function addToQuery(&$query) {
65
		parent::addToQuery($query);
66
		$query->selectField(
67
			"\"{$this->tableName}\".\"{$this->name}ID\"",
68
			"{$this->name}ID"
69
		);
70
		$query->selectField(
71
			"\"{$this->tableName}\".\"{$this->name}Class\"",
72
			"{$this->name}Class"
73
		);
74
	}
75
76
	/**
77
	 * Get the value of the "Class" this key points to
78
	 *
79
	 * @return string Name of a subclass of DataObject
80
	 */
81
	public function getClassValue() {
82
		return $this->classValue;
83
	}
84
85
	/**
86
	 * Set the value of the "Class" this key points to
87
	 *
88
	 * @param string $class Name of a subclass of DataObject
89
	 * @param boolean $markChanged Mark this field as changed?
90
	 */
91
	public function setClassValue($class, $markChanged = true) {
92
		$this->classValue = $class;
93
		if($markChanged) $this->isChanged = true;
94
	}
95
96
	/**
97
	 * Gets the value of the "ID" this key points to
98
	 *
99
	 * @return integer
100
	 */
101
	public function getIDValue() {
102
		return parent::getValue();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getValue() instead of getIDValue()). Are you sure this is correct? If so, you might want to change this to $this->getValue().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
103
	}
104
105
	/**
106
	 * Sets the value of the "ID" this key points to
107
	 *
108
	 * @param integer $id
109
	 * @param boolean $markChanged Mark this field as changed?
110
	 */
111
	public function setIDValue($id, $markChanged = true) {
112
		parent::setValue($id);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (setValue() instead of setIDValue()). Are you sure this is correct? If so, you might want to change this to $this->setValue().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
113
		if($markChanged) $this->isChanged = true;
114
	}
115
116
	public function setValue($value, $record = null, $markChanged = true) {
117
		$idField = "{$this->name}ID";
118
		$classField = "{$this->name}Class";
119
120
		// Check if an object is assigned directly
121
		if($value instanceof DataObject) {
122
			$record = array(
123
				$idField => $value->ID,
124
				$classField => $value->class
125
			);
126
		}
127
128
		// Convert an object to an array
129
		if($record instanceof DataObject) {
130
			$record = $record->getQueriedDatabaseFields();
131
		}
132
133
		// Use $value array if record is missing
134
		if(empty($record) && is_array($value)) {
135
			$record = $value;
136
		}
137
138
		// Inspect presented values
139
		if(isset($record[$idField]) && isset($record[$classField])) {
140
			if(empty($record[$idField]) || empty($record[$classField])) {
141
				$this->setIDValue($this->nullValue(), $markChanged);
142
				$this->setClassValue('', $markChanged);
143
			} else {
144
				$this->setClassValue($record[$classField], $markChanged);
145
				$this->setIDValue($record[$idField], $markChanged);
146
			}
147
		}
148
	}
149
150
	public function getValue() {
151
		if($this->exists()) {
152
			return DataObject::get_by_id($this->getClassValue(), $this->getIDValue());
153
		}
154
	}
155
156
	public function compositeDatabaseFields() {
157
158
		// Ensure the table level cache exists
159
		if(empty(self::$classname_spec_cache[$this->tableName])) {
160
			self::$classname_spec_cache[$this->tableName] = array();
161
		}
162
163
		// Ensure the field level cache exists
164
		if(empty(self::$classname_spec_cache[$this->tableName][$this->name])) {
165
166
			// Get all class names
167
			$classNames = ClassInfo::subclassesFor('DataObject');
168
			unset($classNames['DataObject']);
169
170
			$schema = DB::get_schema();
171
			if($schema->hasField($this->tableName, "{$this->name}Class")) {
172
				$existing = DB::query("SELECT DISTINCT \"{$this->name}Class\" FROM \"{$this->tableName}\"")->column();
173
				$classNames = array_unique(array_merge($classNames, $existing));
174
			}
175
176
			self::$classname_spec_cache[$this->tableName][$this->name]
177
				= "Enum(array('" . implode("', '", array_filter($classNames)) . "'))";
178
		}
179
180
		return array(
181
			'ID' => 'Int',
182
			'Class' => self::$classname_spec_cache[$this->tableName][$this->name]
183
		);
184
	}
185
186
	public function isChanged() {
187
		return $this->isChanged;
188
	}
189
190
	public function exists() {
191
		return $this->getClassValue() && $this->getIDValue();
192
	}
193
194
    public function scalarValueOnly()
195
    {
196
        return false;
197
    }
198
}
199