Annotation.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <?php
  2. namespace Gedmo\Sluggable\Mapping\Driver;
  3. use Gedmo\Mapping\Annotation\SlugHandler;
  4. use Gedmo\Mapping\Annotation\SlugHandlerOption;
  5. use Gedmo\Mapping\Driver\AbstractAnnotationDriver,
  6. Doctrine\Common\Annotations\AnnotationReader,
  7. Gedmo\Exception\InvalidMappingException;
  8. /**
  9. * This is an annotation mapping driver for Sluggable
  10. * behavioral extension. Used for extraction of extended
  11. * metadata from Annotations specificaly for Sluggable
  12. * extension.
  13. *
  14. * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  15. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  16. */
  17. class Annotation extends AbstractAnnotationDriver
  18. {
  19. /**
  20. * Annotation to identify field as one which holds the slug
  21. * together with slug options
  22. */
  23. const SLUG = 'Gedmo\\Mapping\\Annotation\\Slug';
  24. /**
  25. * SlugHandler extension annotation
  26. */
  27. const HANDLER = 'Gedmo\\Mapping\\Annotation\\SlugHandler';
  28. /**
  29. * SlugHandler option annotation
  30. */
  31. const HANDLER_OPTION ='Gedmo\\Mapping\\Annotation\\SlugHandlerOption';
  32. /**
  33. * List of types which are valid for slug and sluggable fields
  34. *
  35. * @var array
  36. */
  37. protected $validTypes = array(
  38. 'string',
  39. 'text',
  40. 'integer',
  41. 'int',
  42. );
  43. /**
  44. * {@inheritDoc}
  45. */
  46. public function readExtendedMetadata($meta, array &$config) {
  47. $class = $this->getMetaReflectionClass($meta);
  48. // property annotations
  49. foreach ($class->getProperties() as $property) {
  50. if ($meta->isMappedSuperclass && !$property->isPrivate() ||
  51. $meta->isInheritedField($property->name) ||
  52. isset($meta->associationMappings[$property->name]['inherited'])
  53. ) {
  54. continue;
  55. }
  56. // slug property
  57. if ($slug = $this->reader->getPropertyAnnotation($property, self::SLUG)) {
  58. $field = $property->getName();
  59. if (!$meta->hasField($field)) {
  60. throw new InvalidMappingException("Unable to find slug [{$field}] as mapped property in entity - {$meta->name}");
  61. }
  62. if (!$this->isValidField($meta, $field)) {
  63. throw new InvalidMappingException("Cannot use field - [{$field}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}");
  64. }
  65. // process slug handlers
  66. $handlers = array();
  67. if (is_array($slug->handlers) && $slug->handlers) {
  68. foreach ($slug->handlers as $handler) {
  69. if (!$handler instanceof SlugHandler) {
  70. throw new InvalidMappingException("SlugHandler: {$handler} should be instance of SlugHandler annotation in entity - {$meta->name}");
  71. }
  72. if (!strlen($handler->class)) {
  73. throw new InvalidMappingException("SlugHandler class: {$handler->class} should be a valid class name in entity - {$meta->name}");
  74. }
  75. $class = $handler->class;
  76. $handlers[$class] = array();
  77. foreach ((array)$handler->options as $option) {
  78. if (!$option instanceof SlugHandlerOption) {
  79. throw new InvalidMappingException("SlugHandlerOption: {$option} should be instance of SlugHandlerOption annotation in entity - {$meta->name}");
  80. }
  81. if (!strlen($option->name)) {
  82. throw new InvalidMappingException("SlugHandlerOption name: {$option->name} should be valid name in entity - {$meta->name}");
  83. }
  84. $handlers[$class][$option->name] = $option->value;
  85. }
  86. $class::validate($handlers[$class], $meta);
  87. }
  88. }
  89. // process slug fields
  90. if (empty($slug->fields) || !is_array($slug->fields)) {
  91. throw new InvalidMappingException("Slug must contain at least one field for slug generation in class - {$meta->name}");
  92. }
  93. foreach ($slug->fields as $slugField) {
  94. if (!$meta->hasField($slugField)) {
  95. throw new InvalidMappingException("Unable to find slug [{$slugField}] as mapped property in entity - {$meta->name}");
  96. }
  97. if (!$this->isValidField($meta, $slugField)) {
  98. throw new InvalidMappingException("Cannot use field - [{$slugField}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}");
  99. }
  100. }
  101. if (!is_bool($slug->updatable)) {
  102. throw new InvalidMappingException("Slug annotation [updatable], type is not valid and must be 'boolean' in class - {$meta->name}");
  103. }
  104. if (!is_bool($slug->unique)) {
  105. throw new InvalidMappingException("Slug annotation [unique], type is not valid and must be 'boolean' in class - {$meta->name}");
  106. }
  107. if (!empty($meta->identifier) && $meta->isIdentifier($field) && !(bool)$slug->unique) {
  108. throw new InvalidMappingException("Identifier field - [{$field}] slug must be unique in order to maintain primary key in class - {$meta->name}");
  109. }
  110. if ($slug->unique === false && $slug->unique_base) {
  111. throw new InvalidMappingException("Slug annotation [unique_base] can not be set if unique is unset or 'false'");
  112. }
  113. if ($slug->unique_base && !$meta->hasField($slug->unique_base) && !$meta->hasAssociation($slug->unique_base)) {
  114. throw new InvalidMappingException("Unable to find [{$slug->unique_base}] as mapped property in entity - {$meta->name}");
  115. }
  116. // set all options
  117. $config['slugs'][$field] = array(
  118. 'fields' => $slug->fields,
  119. 'slug' => $field,
  120. 'style' => $slug->style,
  121. 'updatable' => $slug->updatable,
  122. 'unique' => $slug->unique,
  123. 'unique_base' => $slug->unique_base,
  124. 'separator' => $slug->separator,
  125. 'prefix' => $slug->prefix,
  126. 'suffix' => $slug->suffix,
  127. 'handlers' => $handlers,
  128. );
  129. }
  130. }
  131. }
  132. }