Completed
Push — master ( 0853ea...0eac8f )
by Enrico
02:57
created

AbstractModel::asString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
namespace BOTK\Model;
3
4
abstract class AbstractModel 
5
{
6
	
7
	/**
8
	 * 
9
	 * MUST be redefined by concrete class with the model schema 
10
	 * Each array element is composed by a propery name and and property options.
11
	 * Property option is an array with following (optional) fields:
12
	 * 		'default' 	a value to be used for the propery
13
	 * 		'filter' 	a php filter 
14
	 * 		'options' 	php filter options
15
	 * 		'flags'		php filter flags
16
	 * 
17
	 * Example:array (
18
	 *	'legalName'			=> array(
19
	 *							'filter'    => FILTER_CALLBACK,	
20
	 *	                        'options' 	=> '\BOTK\Filters::FILTER_NORMALIZZE_ADDRESS',
21
	 *		                   ),
22
	 *	'alternateName'		=> array(		
23
                            	'flags'  	=> FILTER_FORCE_ARRAY,
24
	 * 						),
25
	 * 	'postalCode'		=> array(	// italian rules
26
	 *							'filter'    => FILTER_VALIDATE_REGEXP,	
27
	 *	                        'options' 	=> array('regexp'=>'/^[0-9]{5}$/'),
28
	 *                      	'flags'  	=> FILTER_REQUIRE_SCALAR,
29
	 *		                   ),
30
	 * )
31
	 */
32
	protected static $DEFAULT_OPTIONS  = array(
33
		'uri'				=> array(
34
								'filter'    => FILTER_CALLBACK,
35
		                        'options' 	=> '\BOTK\Filters::FILTER_VALIDATE_URI',
36
                            	'flags'  	=> FILTER_REQUIRE_SCALAR,
37
			                   ),
38
		'base'				=> array(
39
								'default'	=> 'urn:local:',
40
								'filter'    => FILTER_CALLBACK,
41
		                        'options' 	=> '\BOTK\Filters::FILTER_VALIDATE_URI',
42
                            	'flags'  	=> FILTER_REQUIRE_SCALAR,
43
			                   ),
44
		'id'				=> array(
45
								'filter'    => FILTER_CALLBACK,
46
		                        'options' 	=> '\BOTK\Filters::FILTER_SANITIZE_ID',
47
                            	'flags'  	=> FILTER_REQUIRE_SCALAR,
48
			                   ),
49
		'page'				=> array(	
50
								'filter'    => FILTER_VALIDATE_URL,
51
                            	'flags'  	=> FILTER_FORCE_ARRAY,
52
			                   ),
53
		'homepage'			=> array(	
54
								'filter'    => FILTER_VALIDATE_URL,
55
                            	'flags'  	=> FILTER_FORCE_ARRAY,
56
			                   ),
57
		'near'				=> array(	
58
								'filter'    => FILTER_CALLBACK,
59
		                        'options' 	=> '\BOTK\Filters::FILTER_VALIDATE_URI',
60
                            	'flags'  	=> FILTER_FORCE_ARRAY,
61
			                   ),
62
		'similarName'		=> array(	
63
								'filter'    => FILTER_CALLBACK,
64
		                        'options' 	=> '\BOTK\Filters::FILTER_VALIDATE_URI',
65
                            	'flags'  	=> FILTER_FORCE_ARRAY,
66
			                   ),
67
	);
68
	
69
	/**
70
	 * known vocabularies
71
	 */
72
	protected static $VOCABULARY  = array(
73
		'rdf'		=> 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
74
		'rdfs'		=> 'http://www.w3.org/2000/01/rdf-schema#',
75
		'owl'		=> 'http://www.w3.org/2002/07/owl#',
76
		'xsd' 		=> 'http://www.w3.org/2001/XMLSchema#',
77
		'dct' 		=> 'http://purl.org/dc/terms/',
78
		'void' 		=> 'http://rdfs.org/ns/void#',
79
		'prov' 		=> 'http://www.w3.org/ns/prov#',
80
		'sd'		=> 'http://www.w3.org/ns/sparql-service-description#',
81
		'schema'	=> 'http://schema.org/',
82
		'wgs' 		=> 'http://www.w3.org/2003/01/geo/wgs84_pos#',
83
		'foaf' 		=> 'http://xmlns.com/foaf/0.1/',
84
		'qb'		=> 'http://purl.org/linked-data/cube#',
85
		'daq'		=> 'http://purl.org/eis/vocab/daq#',
86
		'kees'		=> 'http://linkeddata.center/kees/v1#',
87
		'botk'		=> 'http://botk.linkeddata.center/#',
88
	);
89
	
90
91
	protected $options ;
92
	
93
	protected $data;
94
	protected $rdf =null; //lazy created
95
	protected $tripleCount=0; //lazy created
96
	protected $uniqueIdGenerator=null; // dependency injections
97
	protected $droppedFields = array();
98
	
99
100 27
	protected static function mergeOptions( array $options1, array $options2 )
101
	{
102 27
    	foreach($options2 as $property=>$option){
103
			
104 27
			$options1[$property]=isset($options1[$property])
105 14
				?array_merge($options1[$property], $option)
106 27
				:$option;
107
    	}
108
		
109 27
		return $options1;
110
	}
111
112
113 27
	protected static function constructOptions()
114
	{
115
		//http://stackoverflow.com/questions/22377022/using-array-merge-to-initialize-static-class-variable-in-derived-class-based-on
116 27
		$thisClass = get_called_class();
117 27
		$parentClass = get_parent_class($thisClass);
118 27
		$exists = method_exists($parentClass, __FUNCTION__); 
119 27
		return $exists ? 
120 27
			self::mergeOptions($parentClass::constructOptions(), $thisClass::$DEFAULT_OPTIONS) : 
121 27
			$thisClass::$DEFAULT_OPTIONS;		
122
	}
123
124
125
	/**
126
	 * Do not call directlty constructor, use fromArray or other factory methodsinstead
127
	 */
128 27
    protected function __construct(array $data = array(), array $customOptions = array()) 
129
    { 		
130 27
 		$options = self::mergeOptions(self::constructOptions(),$customOptions);
131
		
132
		// set default values
133 27
		foreach( $options as $property=>$option){	
134 27
			if(empty($data[$property]) && isset($option['default'])){
135 27
				$data[$property] = $option['default'];
136
			}
137
		}
138
139
		// ensure data are sanitized and validated
140 27
		$sanitizedData = array_filter( filter_var_array($data, $options));
141
		
142
		// find and register dropped fields
143 27
		foreach($data as $property=>$value){
144 27
			if($value && empty($sanitizedData[$property])){
145 27
				$this->droppedFields[]=$property;
146
			}
147
		}
148
		
149
		// uri or base MUST be presen
150 27
		if( empty($data['uri']) && empty($data['base']) ){
151
			throw new \InvalidArgumentException("Can't find uri nor base property.");		
152
		}
153
154 27
		$this->options = $options;
155 27
		$this->data = $sanitizedData;
156
		$this->setIdGenerator(function($data){return uniqid();});
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
157 27
    }
158
	
159
	
160
	
161
	/**
162
	 * Create an instance from an associative array
163
	 */
164 27
	public static function fromArray(array $data, array $customOptions = array())
165
	{
166 27
		return new static($data,$customOptions);
167
	}
168
	
169
	
170
	/**
171
	 * Create an instance from an generic standard object
172
	 */
173 3
	public static function fromStdObject( \stdClass $obj, array $customOptions = array())
0 ignored issues
show
Unused Code introduced by
The parameter $customOptions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
174
	{
175 3
		return static::fromArray((array)$obj);
176
	}
177
178
179 4
	public static function getVocabularies()
180
	{
181
		//http://stackoverflow.com/questions/22377022/using-array-merge-to-initialize-static-class-variable-in-derived-class-based-on
182 4
		$thisClass = get_called_class();
183 4
		$parentClass = get_parent_class($thisClass);
184 4
		$exists = method_exists($parentClass, __FUNCTION__); 
185 4
		return $exists ? 
186 4
			array_merge($parentClass::getVocabularies(), $thisClass::$VOCABULARY) : 
187 4
			$thisClass::$VOCABULARY;
188
	}
189
190
	
191 3
	public static function getTurtleHeader($base=null)
192
	{
193 3
		$vocabulariers = static::getVocabularies();
194 3
		$header = empty($base)?'': "@base <$base> .\n";
195 3
		foreach( $vocabulariers as $prefix=>$ns ){
196 3
			$header.="@prefix $prefix: <$ns> .\n";
197
		}
198
		
199 3
		return $header;
200
	}
201
202
	
203 1
	public function getDroppedFields()
204
	{
205 1
		return $this->droppedFields;
206
	}
207
208
	
209
	/**
210
	 * dependecy injection setter 
211
	 */
212 27
	public function setIdGenerator($generator)
213
	{
214 27
		assert( is_callable($generator));
215 27
		$this->uniqueIdGenerator = $generator;
216
		
217 27
		return $this;
218
	}
219
220
221
	/**
222
	 * a generic implementation that use uri, base and id property (all optionals)
223
	 */
224 8
	public function getUri()
225
	{
226 8
		if(!empty($this->data['uri'])){
227 2
			$uri =  $this->data['uri'];
228 6
		} elseif(!empty($this->data['base'])) {
229 6
			$idGenerator=$this->uniqueIdGenerator;
230 6
			$uri = $this->data['base'];
231 6
			$uri.=empty($this->data['id'])?$idGenerator($this->data):$this->data['id'];
232
		} else{
233
			$idGenerator=$this->uniqueIdGenerator;
234
			$uri = 'urn:local:botk:'.$idGenerator($this->data);
235
		}
236
		
237 8
		return $uri;
238
	}
239
240
	
241 2
	public function getOptions()
242
	{
243 2
		return $this->options;
244
	}
245
246
247 4
	public function getTripleCount()
248
	{
249
		// triple count is computed during rdf creation
250 4
		if (!empty($this->data) && is_null($this->rdf)){
251 1
			$this->asTurtleFragment();
252
		}
253
		
254 4
		return $this->tripleCount;
255
	}
256
		
257
258 13
	public function asArray()
259
	{
260 13
		return $this->data;
261
	}	
262
263
264
265 1
	public function asStdObject()
266
	{
267 1
		return (object) $this->asArray();
268
	}
269
	
270
	
271
	/**
272
	 * metadata not yet implemented
273
	 */		
274 1
	public function asLinkedData() 
275
	{
276 1
		return $this->getTurtleHeader() ."\n". $this->asTurtleFragment();
277
	}
278
	
279
	
280 1
	public function asString() 
281
	{
282 1
		return $this->asLinkedData();
283
	}
284
	
285
		
286 1
	public function __toString() 
287
	{
288 1
		return $this->asString();
289
	}
290
	
291
292
	/**
293
	 * this must be implemented
294
	 */
295
	abstract public function asTurtleFragment();
296
}