Overview

Namespaces

  • Alpha
    • Controller
      • Front
    • Exception
    • Model
      • Type
    • Task
    • Util
      • Backup
      • Cache
      • Code
        • Highlight
        • Metric
      • Config
      • Convertor
      • Email
      • Extension
      • Feed
      • File
      • Graph
      • Helper
      • Http
        • Filter
        • Session
      • Image
      • Logging
      • Search
      • Security
    • View
      • Renderer
        • Html
        • Json
      • Widget

Classes

  • Boolean
  • Date
  • DEnum
  • DEnumItem
  • Double
  • Enum
  • Integer
  • Relation
  • RelationLookup
  • Sequence
  • String
  • Text
  • Timestamp
  • Type

Interfaces

  • TypeInterface
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: namespace Alpha\Model\Type;
  4: 
  5: use Alpha\Util\Helper\Validator;
  6: use Alpha\Util\Config\ConfigProvider;
  7: use Alpha\Exception\IllegalArguementException;
  8: use Alpha\Model\ActiveRecord;
  9: use ReflectionClass;
 10: 
 11: /**
 12:  * The Relation complex data type.
 13:  *
 14:  * @since 1.0
 15:  *
 16:  * @author John Collins <dev@alphaframework.org>
 17:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 18:  * @copyright Copyright (c) 2015, John Collins (founder of Alpha Framework).
 19:  * All rights reserved.
 20:  *
 21:  * <pre>
 22:  * Redistribution and use in source and binary forms, with or
 23:  * without modification, are permitted provided that the
 24:  * following conditions are met:
 25:  *
 26:  * * Redistributions of source code must retain the above
 27:  *   copyright notice, this list of conditions and the
 28:  *   following disclaimer.
 29:  * * Redistributions in binary form must reproduce the above
 30:  *   copyright notice, this list of conditions and the
 31:  *   following disclaimer in the documentation and/or other
 32:  *   materials provided with the distribution.
 33:  * * Neither the name of the Alpha Framework nor the names
 34:  *   of its contributors may be used to endorse or promote
 35:  *   products derived from this software without specific
 36:  *   prior written permission.
 37:  *
 38:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 39:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 40:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 41:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 42:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 43:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 44:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 45:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 46:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 47:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 48:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 49:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 50:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 51:  * </pre>
 52:  */
 53: class Relation extends Type implements TypeInterface
 54: {
 55:     /**
 56:      * The name of the business object class which this class is related to.
 57:      *
 58:      * @var string
 59:      *
 60:      * @since 1.0
 61:      */
 62:     private $relatedClass;
 63: 
 64:     /**
 65:      * The name of the fields of the business object class by which this class is related by.
 66:      *
 67:      * @var string
 68:      *
 69:      * @since 1.0
 70:      */
 71:     private $relatedClassField;
 72: 
 73:     /**
 74:      * The name of the field from the related business object class which is displayed by the selection widget.
 75:      *
 76:      * @var string
 77:      *
 78:      * @since 1.0
 79:      */
 80:     private $relatedClassDisplayField;
 81: 
 82:     /**
 83:      * An array of fields to use the values of while rendering related display values via the selection widget.
 84:      *
 85:      * @var array
 86:      *
 87:      * @since 1.0
 88:      */
 89:     private $relatedClassHeaderFields = array();
 90: 
 91:     /**
 92:      * The name of the business object class on the left of a MANY-TO-MANY relation.
 93:      *
 94:      * @var string
 95:      *
 96:      * @since 1.0
 97:      */
 98:     private $relatedClassLeft;
 99: 
100:     /**
101:      * The name of the field from the related business object class on the left of a
102:      * MANY-TO-MANY relation which is displayed by the selection widget.
103:      *
104:      * @var string
105:      *
106:      * @since 1.0
107:      */
108:     private $relatedClassLeftDisplayField;
109: 
110:     /**
111:      * The name of the business object class on the right of a MANY-TO-MANY relation.
112:      *
113:      * @var string
114:      *
115:      * @since 1.0
116:      */
117:     private $relatedClassRight;
118: 
119:     /**
120:      * The name of the field from the related business object class on the right of a
121:      * MANY-TO-MANY relation which is displayed by the selection widget.
122:      *
123:      * @var string
124:      *
125:      * @since 1.0
126:      */
127:     private $relatedClassRightDisplayField;
128: 
129:     /**
130:      * The type of relation ('MANY-TO-ONE' or 'ONE-TO-MANY' or 'ONE-TO-ONE' or 'MANY-TO-MANY').
131:      *
132:      * @var string
133:      *
134:      * @since 1.0
135:      */
136:     private $relationType;
137: 
138:     /**
139:      * In the case of MANY-TO-MANY relationship, a lookup object will be required.
140:      *
141:      * @var Alpha\Model\Type\RelationLookup
142:      *
143:      * @since 1.0
144:      */
145:     private $lookup;
146: 
147:     /**
148:      * In the case of MANY-TO-MANY relationship, this transient array can be used to hold the OIDs of related records.
149:      *
150:      * @var array
151:      *
152:      * @since 2.0
153:      */
154:     private $OIDs = array();
155: 
156:     /**
157:      * When building a relation with the TagObject BO, set this to the name of the tagged class.
158:      *
159:      * @var string
160:      *
161:      * @since 1.0
162:      */
163:     private $taggedClass;
164: 
165:     /**
166:      * An array of the allowable relationship types ('MANY-TO-ONE' or 'ONE-TO-MANY' or 'ONE-TO-ONE' or 'MANY-TO-MANY').
167:      *
168:      * @var array
169:      *
170:      * @since 1.0
171:      */
172:     private $allowableRelationTypes = array('MANY-TO-ONE', 'ONE-TO-MANY', 'ONE-TO-ONE', 'MANY-TO-MANY');
173: 
174:     /**
175:      * The object ID (OID) value of the related object.  In the special case of a MANY-TO-MANY
176:      * relation, contains the OID of the object on the current, accessing side.  Can contain NULL.
177:      *
178:      * @var mixed
179:      *
180:      * @since 1.0
181:      */
182:     private $value = null;
183: 
184:     /**
185:      * The validation rule for the Relation type.
186:      *
187:      * @var string
188:      *
189:      * @since 1.0
190:      */
191:     private $validationRule;
192: 
193:     /**
194:      * The error message for the Relation type when validation fails.
195:      *
196:      * @var string
197:      *
198:      * @since 1.0
199:      */
200:     protected $helper;
201: 
202:     /**
203:      * The size of the value for the this Relation.
204:      *
205:      * @var int
206:      *
207:      * @since 1.0
208:      */
209:     private $size = 11;
210: 
211:     /**
212:      * The absolute maximum size of the value for the this Relation.
213:      *
214:      * @var int
215:      *
216:      * @since 1.0
217:      */
218:     const MAX_SIZE = 11;
219: 
220:     /**
221:      * Constructor.
222:      *
223:      * @since 1.0
224:      */
225:     public function __construct()
226:     {
227:         $this->validationRule = Validator::REQUIRED_INTEGER;
228:         $this->helper = ' not a valid Relation value!  A maximum of '.$this->size.' characters is allowed.';
229:     }
230: 
231:     /**
232:      * Set the name of the business object class that this class is related to.
233:      *
234:      * @param string $RC
235:      * @param string $side Only required for MANY-TO-MANY relations
236:      *
237:      * @since 1.0
238:      *
239:      * @throws Alpha\Exception\IllegalArguementException
240:      */
241:     public function setRelatedClass($RC, $side = '')
242:     {
243:         if (in_array($RC, ActiveRecord::getBOClassNames())) {
244:             switch ($side) {
245:                 case '':
246:                     $this->relatedClass = $RC;
247:                 break;
248:                 case 'left':
249:                     $this->relatedClassLeft = $RC;
250:                 break;
251:                 case 'right':
252:                     $this->relatedClassRight = $RC;
253:                 break;
254:                 default:
255:                     throw new IllegalArguementException('The side paramter ['.$RC.'] is not valid!');
256:             }
257:         } else {
258:             throw new IllegalArguementException('The class ['.$RC.'] is not defined anywhere!');
259:         }
260:     }
261: 
262:     /**
263:      * Get the name of the business object class that this class is related to.
264:      *
265:      * @param string $RC
266:      *
267:      * @return string
268:      *
269:      * @since 1.0
270:      *
271:      * @throws Alpha\Exception\IllegalArguementException
272:      */
273:     public function getRelatedClass($side = '')
274:     {
275:         switch ($side) {
276:             case '':
277:                 return $this->relatedClass;
278:             break;
279:             case 'left':
280:                 return $this->relatedClassLeft;
281:             break;
282:             case 'right':
283:                 return $this->relatedClassRight;
284:             break;
285:             default:
286:                 throw new IllegalArguementException('The side paramter ['.$RC.'] is not valid!');
287: 
288:                 return '';
289:         }
290:     }
291: 
292:     /**
293:      * Setter for the field of the related class.
294:      *
295:      * @param string $RCF
296:      *
297:      * @since 1.0
298:      *
299:      * @throws Alpha\Exception\IllegalArguementException
300:      */
301:     public function setRelatedClassField($RCF)
302:     {
303:         // use reflection to sure the related class has the field $RCF
304:         $reflection = new ReflectionClass($this->relatedClass);
305:         $properties = $reflection->getProperties();
306:         $fieldFound = false;
307: 
308:         foreach ($properties as $propObj) {
309:             if ($RCF == $propObj->name) {
310:                 $fieldFound = true;
311:                 break;
312:             }
313:         }
314: 
315:         if ($fieldFound) {
316:             $this->relatedClassField = $RCF;
317:         } else {
318:             throw new IllegalArguementException('The field ['.$RCF.'] was not found in the class ['.$this->relatedClass.']');
319:         }
320:     }
321: 
322:     /**
323:      * Getter for the field of the related class.
324:      *
325:      * @return string
326:      *
327:      * @since 1.0
328:      */
329:     public function getRelatedClassField()
330:     {
331:         return $this->relatedClassField;
332:     }
333: 
334:     /**
335:      * Setter for ONE-TO-MANY relations, which sets the header fields to
336:      * render from the related class.
337:      *
338:      * @param array $fieldNames
339:      *
340:      * @since 1.0
341:      */
342:     public function setRelatedClassHeaderFields($fieldNames)
343:     {
344:         $this->relatedClassHeaderFields = $fieldNames;
345:     }
346: 
347:     /**
348:      * Getter for the selection widget field headings of the related class.
349:      *
350:      * @return array
351:      *
352:      * @since 1.0
353:      */
354:     public function getRelatedClassHeaderFields()
355:     {
356:         return $this->relatedClassHeaderFields;
357:     }
358: 
359:     /**
360:      * Setter for the display field from the related class.
361:      *
362:      * @param string $RCDF
363:      * @param string $side Only required for MANY-TO-MANY relations
364:      *
365:      * @since 1.0
366:      *
367:      * @throws Alpha\Exception\IllegalArguementException
368:      */
369:     public function setRelatedClassDisplayField($RCDF, $side = '')
370:     {
371:         switch ($side) {
372:             case '':
373:                 $this->relatedClassDisplayField = $RCDF;
374:             break;
375:             case 'left':
376:                 $this->relatedClassLeftDisplayField = $RCDF;
377:             break;
378:             case 'right':
379:                 $this->relatedClassRightDisplayField = $RCDF;
380:             break;
381:             default:
382:                 throw new IllegalArguementException('The side paramter ['.$RC.'] is not valid!');
383:         }
384:     }
385: 
386:     /**
387:      * Getter for the display field from the related class.
388:      *
389:      * @param string $side Only required for MANY-TO-MANY relations
390:      *
391:      * @return string
392:      *
393:      * @since 1.0
394:      *
395:      * @throws Alpha\Exception\IllegalArguementException
396:      */
397:     public function getRelatedClassDisplayField($side = '')
398:     {
399:         switch ($side) {
400:             case '':
401:                 return $this->relatedClassDisplayField;
402:             break;
403:             case 'left':
404:                 return $this->relatedClassLeftDisplayField;
405:             break;
406:             case 'right':
407:                 return $this->relatedClassRightDisplayField;
408:             break;
409:             default:
410:                 throw new IllegalArguementException('The side paramter ['.$RC.'] is not valid!');
411: 
412:                 return '';
413:         }
414:     }
415: 
416:     /**
417:      * Setter for the relation type.
418:      *
419:      * @param string $RT
420:      *
421:      * @throws Alpha\Exception\IllegalArguementException
422:      * @throws Alpha\Exception\FailedLookupCreateException
423:      *
424:      * @since 1.0
425:      */
426:     public function setRelationType($RT)
427:     {
428:         if (in_array($RT, $this->allowableRelationTypes)) {
429:             $this->relationType = $RT;
430:             if ($RT == 'MANY-TO-MANY') {
431:                 try {
432:                     $this->lookup = new RelationLookup($this->relatedClassLeft, $this->relatedClassRight);
433:                 } catch (FailedLookupCreateException $flce) {
434:                     throw $flce;
435:                 } catch (IllegalArguementException $iae) {
436:                     throw $iae;
437:                 }
438:             }
439:         } else {
440:             throw new IllegalArguementException('Relation type of ['.$RT.'] is invalid!');
441:         }
442:     }
443: 
444:     /**
445:      * Getter for the relation type.
446:      *
447:      * @return string
448:      *
449:      * @since 1.0
450:      */
451:     public function getRelationType()
452:     {
453:         return $this->relationType;
454:     }
455: 
456:     /**
457:      * Setter for the value (OID of related object) of this relation.
458:      *
459:      * @param int $val
460:      *
461:      * @since 1.0
462:      *
463:      * @throws Alpha\Exception\IllegalArguementException
464:      */
465:     public function setValue($val)
466:     {
467:         if (empty($val)) {
468:             $this->value = null;
469:         } else {
470:             if (!Validator::isInteger($val)) {
471:                 throw new IllegalArguementException("[$val]".$this->helper);
472:             }
473: 
474:             if (mb_strlen($val) <= $this->size) {
475:                 $this->value = str_pad($val, 11, '0', STR_PAD_LEFT);
476:             } else {
477:                 throw new IllegalArguementException("[$val]".$this->helper);
478:             }
479:         }
480:     }
481: 
482:     /**
483:      * Getter for the array of OIDs used by MANY-TO-MANY instances.
484:      *
485:      * @return array
486:      *
487:      * @since 2.0
488:      */
489:     public function getRelatedOIDs()
490:     {
491:         return $this->OIDs;
492:     }
493: 
494:     /**
495:      * Setter for the array of OIDs used by MANY-TO-MANY instances.
496:      *
497:      * @param array $OIDs
498:      *
499:      * @since 2.0
500:      *
501:      * @throws Alpha\Exception\IllegalArguementException
502:      */
503:     public function setRelatedOIDs($OIDs)
504:     {
505:         if (is_array($OIDs)) {
506:             $this->OIDs = $OIDs;
507:         } else {
508:             throw new IllegalArguementException('An array must be provided to setRelatedOIDs()!');
509:         }
510:     }
511: 
512:     /**
513:      * Getter for the Relation value.
514:      *
515:      * @return mixed
516:      *
517:      * @since 1.0
518:      */
519:     public function getValue()
520:     {
521:         return $this->value;
522:     }
523: 
524:     /**
525:      * Get the validation rule.
526:      *
527:      * @return string
528:      *
529:      * @since 1.0
530:      */
531:     public function getRule()
532:     {
533:         return $this->validationRule;
534:     }
535: 
536:     /**
537:      * Setter to override the default validation rule.
538:      *
539:      * @param string $rule
540:      *
541:      * @since 1.0
542:      */
543:     public function setRule($rule)
544:     {
545:         $this->validationRule = $rule;
546:     }
547: 
548:     /**
549:      * Getter for the display value of the related class field.  In the case of a
550:      * MANY-TO-MANY Relation, a comma-seperated sorted list of values is returned.
551:      *
552:      * @param string $accessingClassName Used to indicate the reading side when accessing from MANY-TO-MANY relation (leave blank for other relation types)
553:      *
554:      * @return string
555:      *
556:      * @since 1.0
557:      *
558:      * @throws Alpha\Exception\IllegalArguementException
559:      */
560:     public function getRelatedClassDisplayFieldValue($accessingClassName = '')
561:     {
562:         if ($this->relationType == 'MANY-TO-MANY') {
563:             /*
564:              * 1. Use RelationLookup to get OIDs of related objects
565:              * 2. Load related objects
566:              * 3. Access the value of the field on the object to build the
567:              * comma-seperated list.
568:              */
569:             if (empty($this->lookup)) {
570:                 throw new IllegalArguementException('Tried to load related MANY-TO-MANY fields but no RelationLookup set on the Relation object!');
571:             }
572: 
573:             if (empty($accessingClassName)) {
574:                 throw new IllegalArguementException('Tried to load related MANY-TO-MANY fields but no accessingClassName parameter set on the call to getRelatedClassDisplayFieldValue!');
575:             }
576: 
577:             // load objects on the right from accessing on the left
578:             if ($accessingClassName == $this->relatedClassLeft) {
579:                 $obj = new $this->relatedClassRight();
580: 
581:                 $lookupObjects = $this->lookup->loadAllByAttribute('leftID', $this->value);
582: 
583:                 $values = array();
584:                 foreach ($lookupObjects as $lookupObject) {
585:                     $obj->load($lookupObject->get('rightID'));
586:                     array_push($values, $obj->get($this->relatedClassRightDisplayField));
587:                 }
588:                 // sort array, then return as comma-seperated string
589:                 asort($values);
590: 
591:                 return implode(',', $values);
592:             }
593:             // load objects on the left from accessing on the right
594:             if ($accessingClassName == $this->relatedClassRight) {
595:                 $obj = new $this->relatedClassLeft();
596: 
597:                 $lookupObjects = $this->lookup->loadAllByAttribute('rightID', $this->value);
598: 
599:                 $values = array();
600:                 foreach ($lookupObjects as $lookupObject) {
601:                     $obj->load($lookupObject->get('leftID'));
602:                     array_push($values, $obj->get($this->relatedClassLeftDisplayField));
603:                 }
604:                 // sort array, then return as comma-seperated string
605:                 asort($values);
606: 
607:                 return implode(',', $values);
608:             }
609:         } else {
610:             $obj = new $this->relatedClass();
611:             // making sure we have an object to load
612:             if (empty($this->value) || $this->value == '00000000000') {
613:                 return '';
614:             } else {
615:                 $obj->load($this->value);
616: 
617:                 return $obj->get($this->relatedClassDisplayField);
618:             }
619:         }
620:     }
621: 
622:     /**
623:      * For one-to-many and many-to-many relations, get the objects on the other side.
624:      *
625:      * string $accessingClassName Used to indicate the reading side when accessing from MANY-TO-MANY relation (leave blank for other relation types)
626:      *
627:      * @return array
628:      *
629:      * @since 1.0
630:      *
631:      * @throws Alpha\Exception\IllegalArguementException
632:      */
633:     public function getRelatedObjects($accessingClassName = '')
634:     {
635:         $config = ConfigProvider::getInstance();
636: 
637:         if ($this->relationType == 'ONE-TO-MANY') {
638:             if ($this->getValue() == '') { // if the value is empty, then return an empty array
639:                 return array();
640:             }
641: 
642:             $obj = new $this->relatedClass();
643:             if ($this->relatedClass == 'Alpha\Model\Tag') {
644:                 $objects = $obj->loadTags($this->taggedClass, $this->getValue());
645:             } else {
646:                 $objects = $obj->loadAllByAttribute($this->getRelatedClassField(), $this->getValue());
647:             }
648: 
649:             return $objects;
650:         } else { // MANY-TO-MANY
651:             if (empty($this->lookup)) {
652:                 throw new IllegalArguementException('Tried to load related MANY-TO-MANY objects but no RelationLookup set on the Relation object!');
653:             }
654: 
655:             if (empty($accessingClassName)) {
656:                 throw new IllegalArguementException('Tried to load related MANY-TO-MANY objects but no accessingClassName parameter set on the call to getRelatedObjects!');
657:             }
658: 
659:             $objects = array();
660: 
661:             // load objects on the right from accessing on the left
662:             if ($accessingClassName == $this->relatedClassLeft) {
663:                 $lookupObjects = $this->lookup->loadAllByAttribute('leftID', $this->value);
664: 
665:                 foreach ($lookupObjects as $lookupObject) {
666:                     $obj = new $this->relatedClassRight();
667:                     $obj->load($lookupObject->get('rightID'));
668:                     array_push($objects, $obj);
669:                 }
670:             }
671:             // load objects on the left from accessing on the right
672:             if ($accessingClassName == $this->relatedClassRight && count($objects) == 0) {
673:                 $lookupObjects = $this->lookup->loadAllByAttribute('rightID', $this->value);
674: 
675:                 foreach ($lookupObjects as $lookupObject) {
676:                     $obj = new $this->relatedClassLeft();
677:                     $obj->load($lookupObject->get('leftID'));
678:                     array_push($objects, $obj);
679:                 }
680:             }
681: 
682:             return $objects;
683:         }
684:     }
685: 
686:     /**
687:      * For one-to-one relations, get the object on the other side.
688:      *
689:      * @return array
690:      *
691:      * @since 1.0
692:      *
693:      * @throws Alpha\Model\Type\IllegalArguementException
694:      */
695:     public function getRelatedObject()
696:     {
697:         if (!class_exists($this->relatedClass)) {
698:             throw new IllegalArguementException('Could not load the definition for the BO class ['.$this->relatedClass.']');
699:         }
700: 
701:         $obj = new $this->relatedClass();
702:         $obj->loadByAttribute($this->getRelatedClassField(), $this->getValue());
703: 
704:         return $obj;
705:     }
706: 
707:     /**
708:      * Get the allowable size of the Relation in the database field.
709:      *
710:      * @return int
711:      *
712:      * @since 1.0
713:      */
714:     public function getSize()
715:     {
716:         return $this->size;
717:     }
718: 
719:     /**
720:      * Get the lookup object if available (only on MANY-TO-MANY relations, null otherwise).
721:      *
722:      * @return RelationLookup
723:      *
724:      * @since 1.0
725:      */
726:     public function getLookup()
727:     {
728:         return $this->lookup;
729:     }
730: 
731:     /**
732:      * Gets the side ('left' or 'right') of the passed classname on the current Relation object.
733:      *
734:      * @param string $BOClassname
735:      *
736:      * @return string
737:      *
738:      * @since 1.0
739:      *
740:      * @throws Alpha\Model\Type\IllegalArguementException
741:      */
742:     public function getSide($BOClassname)
743:     {
744:         if ($BOClassname == $this->relatedClassLeft) {
745:             return 'left';
746:         } elseif ($BOClassname == $this->relatedClassRight) {
747:             return 'right';
748:         } else {
749:             throw new IllegalArguementException('Error trying to determine the MANY-TO-MANY relationship side for the classname ['.$BOClassname.']');
750:         }
751:     }
752: 
753:     /**
754:      * Set the taggedClass property to the name of the tagged class when building relations
755:      * to the TagObject BO.
756:      *
757:      * @param $taggedClass
758:      *
759:      * @since 1.0
760:      */
761:     public function setTaggedClass($taggedClass)
762:     {
763:         $this->taggedClass = $taggedClass;
764:     }
765: }
766: 
Alpha Framework 2.0.4 API Documentation API documentation generated by ApiGen 2.8.0