PageRenderTime 54ms CodeModel.GetById 11ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 1ms

/app/plugins/Db/libraries/Atomik/Model/Builder.php

https://bitbucket.org/fbertagnin/fbwork4
PHP | 625 lines | 295 code | 66 blank | 264 comment | 32 complexity | b7c8e85b9a569f0b0cfc6d0d09f0fae8 MD5 | raw file
  1<?php
  2/**
  3 * Atomik Framework
  4 * Copyright (c) 2008-2009 Maxime Bouroumeau-Fuseau
  5 *
  6 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  7 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  8 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  9 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 10 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 11 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 12 * THE SOFTWARE.
 13 *
 14 * @package Atomik
 15 * @subpackage Model
 16 * @author Maxime Bouroumeau-Fuseau
 17 * @copyright 2008-2009 (c) Maxime Bouroumeau-Fuseau
 18 * @license http://www.opensource.org/licenses/mit-license.php
 19 * @link http://www.atomikframework.com
 20 */
 21
 22/** Atomik_Options */
 23require_once 'Atomik/Options.php';
 24
 25/** Atomik_Model_Manager */
 26require_once 'Atomik/Model/Manager.php';
 27
 28/** Atomik_Model_Field_Abstract */
 29require_once 'Atomik/Model/Field/Abstract.php';
 30
 31/** Atomik_Model_Builder_Reference */
 32require_once 'Atomik/Model/Builder/Reference.php';
 33
 34/** Atomik_Model_Builder_Link */
 35require_once 'Atomik/Model/Builder/Link.php';
 36
 37/** Atomik_Model_Behaviour_Broker */
 38require_once 'Atomik/Model/Behaviour/Broker.php';
 39
 40/**
 41 * A model builder can be used to programatically build models
 42 * 
 43 * @package Atomik
 44 * @subpackage Model
 45 */
 46class Atomik_Model_Builder extends Atomik_Options
 47{
 48	/**
 49	 * @var string
 50	 */
 51	public $name;
 52	
 53	/**
 54	 * @var string
 55	 */
 56	public $className;
 57	
 58	/**
 59	 * @var string
 60	 */
 61	public $tableName;
 62	
 63	/**
 64	 * @var Atomik_Model_Manager
 65	 */
 66	protected $_manager;
 67	
 68	/**
 69	 * @var Atomik_Model_Builder
 70	 */
 71	protected $_parentModelBuilder;
 72	
 73	/**
 74	 * @var string
 75	 */
 76	protected $_inheritanceType;
 77	
 78	/**
 79	 * @var array
 80	 */
 81	protected $_fields = array();
 82	
 83	/**
 84	 * @var bool
 85	 */
 86	protected $_autoPrimaryKey = true;
 87	
 88	/**
 89	 * @var Atomik_Model_Field_Abstract
 90	 */
 91	protected $_primaryKeyField;
 92	
 93	/**
 94	 * @var array
 95	 */
 96	protected $_references = array();
 97	
 98	/**
 99	 * @var Atomik_Model_Behaviour_Broker
100	 */
101	protected $_behaviourBroker = array();
102	
103	/**
104	 * @var array
105	 */
106	protected $_links = array();
107	
108	/**
109	 * Constructor
110	 *
111	 * @param 	string 	$name
112	 * @param 	array 	$metadata
113	 */
114	public function __construct($name, $className = null, $tableName = null)
115	{
116		$this->name = $name;
117		$this->className = $className;
118		$this->tableName = $tableName === null ? $name : $tableName;
119		$this->_behaviourBroker = new Atomik_Model_Behaviour_Broker($this);
120		$this->setPrimaryKeyField();
121	}
122	
123	/**
124	 * Sets the manager associated to this builder
125	 * 
126	 * @param Atomik_Model_Manager $manager
127	 */
128	public function setManager(Atomik_Model_Manager $manager = null)
129	{
130		if ($manager === null) {
131			$manager = Atomik_Model_Manager::getDefault();
132		}
133		$this->_manager = $manager;
134	}
135	
136	/**
137	 * Returns the associated model manager
138	 * 
139	 * @return Atomik_Model_Manager
140	 */
141	public function getManager()
142	{
143		if ($this->_manager === null) {
144			$this->setManager();
145		}
146		return $this->_manager;
147	}
148	
149	/**
150	 * Sets the parent model
151	 * 
152	 * @param	string|Atomik_Model_Builder	$parentModel
153	 */
154	public function setParentModel($parentModel)
155	{
156		$parent = Atomik_Model_Builder_Factory::get($parentModel);
157		$type = $parent->getOption('inheritance', 'abstract');
158		
159		switch($type) {
160			case 'none':
161				return;
162				
163			case 'abstract':
164				$this->_fields = array_merge($parent->getFields(), $this->_fields);
165				$this->_options = array_merge($parent->getOptions(), $this->_options);
166				foreach ($parent->getBehaviourBroker()->getBehaviours() as $behaviour) {
167					$this->_behaviourBroker->addBehaviour(clone $behaviour);
168				}
169				foreach ($parent->getReferences() as $ref) {
170					$this->addReference(clone $ref);
171				}
172				foreach ($parent->getLinks() as $link) {
173					$this->addLink(clone $link);
174				}
175				break;
176				
177			case 'reference':
178				$ref = new Atomik_Model_Builder_Reference('parent', Atomik_Model_Builder_Reference::HAS_PARENT);
179				$ref->target = $parent;
180				$ref->targetField = $parent->getPrimaryKeyField()->name;
181				$ref->sourceField = $ref->target . '_' . $ref->targetField;
182				$this->addReference($ref);
183				break;
184		}
185		
186		$this->_parentModelBuilder = $parent;
187		$this->_inheritanceType = $type;
188	}
189	
190	/**
191	 * Checks if it as a parent
192	 * 
193	 * @return bool
194	 */
195	public function hasParentModel()
196	{
197		return $this->_parentModelBuilder !== null;
198	}
199	
200	/**
201	 * Returns the parent model builder or null
202	 * 
203	 * @return Atomik_Model_Builder
204	 */
205	public function getParentModel()
206	{
207		return $this->_parentModelBuilder;
208	}
209	
210	/**
211	 * Returns the type of inheritance used
212	 * 
213	 * @return string
214	 */
215	public function getInheritanceType()
216	{
217		return $this->_inheritanceType;
218	}
219	
220	/**
221	 * Resets all the fields
222	 *
223	 * @param array $fields
224	 */
225	public function setFields($fields = array())
226	{
227		$this->_fields = array();
228		foreach ($fields as $field) {
229			$this->addField($field);
230		}
231	}
232	
233	/**
234	 * Adds a new field
235	 *
236	 * @param Atomik_Model_Field_Abstract $field
237	 */
238	public function addField(Atomik_Model_Field_Abstract $field)
239	{
240		$this->_fields[$field->name] = $field;
241		
242		if ($field->getOption('primary-key', false)) {
243			$this->setPrimaryKeyField($field);
244		}
245	}
246	
247	/**
248	 * Checks if a field exists
249	 *
250	 * @param 	string $name
251	 * @return 	bool
252	 */
253	public function hasField($name)
254	{
255		return isset($this->_fields[(string) $name]);
256	}
257	
258	/**
259	 * Returns a field object
260	 *
261	 * @param 	string 	$name
262	 * @return 	Atomik_Model_Field_Abstract|bool	False if the field does not exist
263	 */
264	public function getField($name)
265	{
266		if (!isset($this->_fields[$name])) {
267			return false;
268		}
269		return $this->_fields[$name];
270	}
271	
272	/**
273	 * Returns the field with the specifiec option
274	 * 
275	 * @param	string	 $option
276	 * @return 	Atomik_Model_Field_Abstract
277	 */
278	public function getFieldWithOption($option)
279	{
280		foreach ($this->_fields as $field) {
281			if ($field->hasOption($option)) {
282				return $field;
283			}
284		}
285		return null;
286	}
287	
288	/**
289	 * Returns all fields
290	 *
291	 * @return array
292	 */
293	public function getFields()
294	{
295		return $this->_fields;
296	}
297	
298	/**
299	 * Sets the primary key field
300	 * 
301	 * If $field is null, will use or create a field named id
302	 * 
303	 * @param Atomik_Model_Field_Abstract $field
304	 */
305	public function setPrimaryKeyField(Atomik_Model_Field_Abstract $field = null)
306	{
307		$removeAutoKey = true;
308		
309		if ($field === null) {
310			if ($this->_primaryKeyField !== null) {
311				// primary key already defined
312				return;
313			}
314			
315			// checks if there is a field named id
316			if (($field = $this->getField('id')) === false) {
317				require_once 'Atomik/Model/Field.php';
318				$field = new Atomik_Model_Field('id', 'int', array('form-ignore' => true));
319				$this->addField($field);
320				$this->_autoPrimaryKey = true;
321				$removeAutoKey = false;
322			}
323		}
324		
325		if ($removeAutoKey && $this->_autoPrimaryKey) {
326			unset($this->_fields['id']);
327			$this->_autoPrimaryKey = false;
328		}
329		
330		$this->_primaryKeyField = $field;
331	}
332	
333	/**
334	 * Returns the field used as the primary key.
335	 *
336	 * @return Atomik_Model_Field_Abstract
337	 */
338	public function getPrimaryKeyField()
339	{
340		if ($this->_primaryKeyField === null) {
341			$this->setPrimaryKeyField();
342		}
343		return $this->_primaryKeyField;
344	}
345	
346	/**
347	 * Checks if a field is the primary key
348	 * 
349	 * @param Atomik_Model_Field_Abstract $field
350	 * @return bool
351	 */
352	public function isFieldThePrimaryKey(Atomik_Model_Field_Abstract $field)
353	{
354		return $this->_primaryKeyField == $field;
355	}
356	
357	/**
358	 * Resets all the references
359	 *
360	 * @param array $references
361	 */
362	public function setReferences($references = array())
363	{
364		$this->_references = array();
365		foreach ($references as $reference) {
366			$this->addReference($reference);
367		}
368	}
369	
370	/**
371	 * Adds a new reference
372	 * 
373	 * @param	Atomik_Model_Builder_Reference	$reference
374	 */
375	public function addReference(Atomik_Model_Builder_Reference $reference)
376	{
377		if (!$this->hasField($reference->sourceField)) {
378			$this->addField(new Atomik_Model_Field($reference->sourceField, 'int'));
379		}
380		$this->_references[$reference->name] = $reference;
381	}
382	
383	/**
384	 * Checks if a reference exists
385	 * 
386	 * @param	string	$name
387	 * @return 	bool
388	 */
389	public function hasReference($name)
390	{
391		return isset($this->_references[$name]);
392	}
393	
394	/**
395	 * Returns a reference object
396	 *
397	 * @param 	string 		$name 
398	 * @return 	Atomik_Model_Builder_Reference|bool 	False if not found
399	 */
400	public function getReference($name)
401	{
402		if (!isset($this->_references[$name])) {
403			return false;
404		}
405		return $this->_references[$name];
406	}
407	
408	/**
409	 * Returns a reference from the source field
410	 * 
411	 * @param 	Atomik_Model_Field_Abstract 		$field
412	 * @return 	Atomik_Model_Builder_Reference
413	 */
414	public function getReferenceFromSourceField(Atomik_Model_Field_Abstract $field)
415	{
416		foreach ($this->_references as $reference) {
417			if ($reference->sourceField == $field->name) {
418				return $reference;
419			}
420		}
421	}
422	
423	/**
424	 * Returns all references or only the one associated to a model
425	 *
426	 * @param 	string 	$modelName
427	 * @param 	string 	$type 		Reference type
428	 * @return 	array
429	 */
430	public function getReferences($targetModel = null, $type = null)
431	{
432		if ($targetModel === null) {
433			return $this->_references;
434		}
435		
436		$references = array();
437		foreach ($this->_references as $reference) {
438			if ($reference->isTarget($targetModel) && ($type === null || $reference->type == $type)) {
439				$references[] = $reference;
440			}
441		}
442		return $references;
443	}
444	
445	/**
446	 * Checks if a field is part of a reference
447	 * 
448	 * @param Atomik_Model_Field_Abstract $field
449	 * @return bool
450	 */
451	public function isFieldPartOfReference(Atomik_Model_Field_Abstract $field)
452	{
453		foreach ($this->_references as $reference) {
454			if ($reference->sourceField == $field->name) {
455				return true;
456			}
457		}
458		return false;
459	}
460	
461	/**
462	 * Checks if the specified model is related to this one
463	 * 
464	 * @param Atomik_Model_Builder $builder
465	 * @return bool
466	 */
467	public function isModelRelated(Atomik_Model_Builder $builder)
468	{
469		foreach ($this->_references as $reference) {
470			if ($reference->isTarget($builder)) {
471				return true;
472			}
473		}
474		return false;
475	}
476	
477	/**
478	 * Checks if the specified model is a child of this one
479	 * 
480	 * @param Atomik_Model_Builder $builder
481	 * @return bool
482	 */
483	public function isChildModel(Atomik_Model_Builder $builder)
484	{
485		foreach ($this->_references as $reference) {
486			if ($reference->isTarget($builder) && $reference->type != Atomik_Model_Builder_Reference::HAS_PARENT) {
487				return true;
488			}
489		}
490		return false;
491	}
492	
493	/**
494	 * Checks if the specified model is the parent of this one
495	 * 
496	 * @param Atomik_Model_Builder $builder
497	 * @return bool
498	 */
499	public function isParentModel(Atomik_Model_Builder $builder)
500	{
501		foreach ($this->_references as $reference) {
502			if ($reference->isTarget($builder) && $reference->type == Atomik_Model_Builder_Reference::HAS_PARENT) {
503				return true;
504			}
505		}
506		return false;
507	}
508	
509	/**
510	 * Returns the behaviour broker
511	 * 
512	 * @return Atomik_Model_Behaviour_Broker
513	 */
514	public function getBehaviourBroker()
515	{
516		return $this->_behaviourBroker;
517	}
518	
519	/**
520	 * Resets all links
521	 * 
522	 * @param array $links
523	 */
524	public function setLinks($links)
525	{
526		$this->_links = array();
527		foreach ($links as $link) {
528			$this->addLink($link);
529		}
530	}
531	
532	/**
533	 * Adds a new link
534	 * 
535	 * @param Atomik_Model_Builder_Link $link
536	 */
537	public function addLink(Atomik_Model_Builder_Link $link)
538	{
539		$this->_links[$link->name] = $link;
540	}
541	
542	/**
543	 * Checks if this builder as the specified link
544	 * 
545	 * @param string $name
546	 */
547	public function hasLink($name)
548	{
549		return isset($this->_links[$name]);
550	}
551	
552	/**
553	 * Returns the link with the specified name
554	 * 
555	 * @param string $name
556	 */
557	public function getLink($name)
558	{
559		if (!isset($this->_links[$name])) {
560			return null;
561		}
562		return $this->_links[$name];
563	}
564	
565	/**
566	 * Returns all links
567	 * 
568	 * @return array
569	 */
570	public function getLinks()
571	{
572		return $this->_links;
573	}
574	
575	/**
576	 * Creates a model instance
577	 *
578	 * @see Atomik_Model::__construct()
579	 * @param 	array 			$values
580	 * @param 	bool 			$new	
581	 * @return 	Atomik_Model
582	 */
583	public function createInstance($values = array(), $new = true)
584	{
585		$className = $this->className;
586		if ($className === null) {
587			/** Atomik_Model */
588			require_once 'Atomik/Model.php';
589			$className = 'Atomik_Model';
590		}
591		
592		$dataToFilter = array_intersect_key($values, $this->_fields);
593		$data = array_diff_key($values, $this->_fields);
594		foreach ($dataToFilter as $key => $value) {
595			$data[$key] = $this->_fields[$key]->filterInput($value);
596		}
597		
598		$this->_behaviourBroker->notifyBeforeCreateInstance($this, $data, $new);
599		$instance = new $className($data, $new, $this);
600		$this->_behaviourBroker->notifyAfterCreateInstance($this, $instance);
601		
602		return $instance;
603	}
604	
605	/**
606	 * Returns a form for this builder
607	 *
608	 * @return Atomik_Model_Form
609	 */
610	public function getForm()
611	{
612		require_once 'Atomik/Model/Form.php';
613		return new Atomik_Model_Form($this);
614	}
615	
616	/**
617	 * Returns the builder name
618	 * 
619	 * @return string
620	 */
621	public function __toString()
622	{
623		return $this->name;
624	}
625}