3 namespace Drupal\crop\Entity;
5 use Drupal\Core\Entity\ContentEntityBase;
6 use Drupal\Core\Entity\EntityStorageInterface;
7 use Drupal\Core\Entity\EntityTypeInterface;
8 use Drupal\Core\Field\BaseFieldDefinition;
9 use Drupal\crop\CropInterface;
10 use Drupal\crop\EntityProviderNotFoundException;
13 * Defines the crop entity class.
17 * label = @Translation("Crop"),
18 * bundle_label = @Translation("Crop type"),
20 * "storage" = "Drupal\crop\CropStorage",
21 * "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
22 * "access" = "Drupal\Core\Entity\EntityAccessControlHandler",
24 * "default" = "Drupal\Core\Entity\ContentEntityForm",
25 * "delete" = "Drupal\Core\Entity\ContentEntityConfirmFormBase",
26 * "edit" = "Drupal\Core\Entity\ContentEntityForm"
28 * "translation" = "Drupal\content_translation\ContentTranslationHandler"
30 * base_table = "crop",
31 * data_table = "crop_field_data",
32 * revision_table = "crop_revision",
33 * revision_data_table = "crop_field_revision",
35 * translatable = TRUE,
36 * render_cache = FALSE,
41 * "langcode" = "langcode",
44 * bundle_entity_type = "crop_type",
45 * permission_granularity = "entity_type",
46 * admin_permission = "administer crop",
51 class Crop extends ContentEntityBase implements CropInterface {
56 public function position() {
58 'x' => (int) $this->x->value,
59 'y' => (int) $this->y->value,
66 public function setPosition($x, $y) {
76 public function anchor() {
78 'x' => (int) ($this->x->value - ($this->width->value / 2)),
79 'y' => (int) ($this->y->value - ($this->height->value / 2)),
86 public function size() {
88 'width' => (int) $this->width->value,
89 'height' => (int) $this->height->value,
96 public function setSize($width, $height) {
97 $this->width = $width;
98 $this->height = $height;
105 public function provider() {
106 /** @var \Drupal\crop\EntityProviderManager $plugin_manager */
107 $plugin_manager = \Drupal::service('plugin.manager.crop.entity_provider');
109 if (!$plugin_manager->hasDefinition($this->entity_type->value)) {
110 throw new EntityProviderNotFoundException(t('Entity provider @id not found.', ['@id' => $this->entity_type->value]));
113 return $plugin_manager->createInstance($this->entity_type->value);
119 public static function cropExists($uri, $type = NULL) {
120 $query = \Drupal::entityQuery('crop')
121 ->condition('uri', $uri);
123 $query->condition('type', $type);
125 return (bool) $query->execute();
131 public static function findCrop($uri, $type) {
132 $query = \Drupal::entityQuery('crop')
133 ->condition('uri', $uri);
135 $query->condition('type', $type);
137 $crop = $query->sort('cid')
141 return $crop ? \Drupal::entityTypeManager()->getStorage('crop')->load(current($crop)) : NULL;
147 public function preSave(EntityStorageInterface $storage) {
148 parent::preSave($storage);
150 // If no revision author has been set explicitly, make the current user
152 if (!$this->get('revision_uid')->entity) {
153 $this->set('revision_uid', \Drupal::currentUser()->id());
156 // Try to set URI if not yet defined.
157 if (empty($this->uri->value) && !empty($this->entity_type->value) && !empty($this->entity_id->value)) {
158 $entity = \Drupal::entityTypeManager()->getStorage($this->entity_type->value)->load($this->entity_id->value);
159 if ($uri = $this->provider()->uri($entity)) {
160 $this->set('uri', $uri);
168 public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
169 parent::preSaveRevision($storage, $record);
171 if (!$this->isNewRevision() && isset($this->original) && (!isset($record->revision_log) || $record->revision_log === '')) {
172 // If we are updating an existing crop without adding a new revision, we
173 // need to make sure $entity->revision_log is reset whenever it is empty.
174 // Therefore, this code allows us to avoid clobbering an existing log
175 // entry with an empty one.
176 $record->revision_log = $this->original->revision_log->value;
183 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
184 parent::postSave($storage, $update);
186 // If you are manually generating your image derivatives instead of waiting
187 // for them to be generated on the fly, because you are using a cloud
188 // storage service (like S3), then you may not want your image derivatives
189 // to be flushed. If they are you could end up serving 404s during the time
190 // between the crop entity being saved and the image derivative being
191 // manually generated and pushed to your cloud storage service. In that
192 // case, set this configuration variable to false.
193 $flush_derivative_images = \Drupal::config('crop.settings')->get('flush_derivative_images');
194 if ($flush_derivative_images) {
195 image_path_flush($this->uri->value);
202 public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
205 $fields['cid'] = BaseFieldDefinition::create('integer')
206 ->setLabel(t('Crop ID'))
207 ->setDescription(t('The crop ID.'))
209 ->setSetting('unsigned', TRUE);
211 $fields['uuid'] = BaseFieldDefinition::create('uuid')
212 ->setLabel(t('UUID'))
213 ->setDescription(t('The crop UUID.'))
216 $fields['vid'] = BaseFieldDefinition::create('integer')
217 ->setLabel(t('Revision ID'))
218 ->setDescription(t('The crop revision ID.'))
220 ->setSetting('unsigned', TRUE);
222 $fields['type'] = BaseFieldDefinition::create('entity_reference')
223 ->setLabel(t('Type'))
224 ->setDescription(t('The crop type.'))
225 ->setSetting('target_type', 'crop_type')
228 $fields['langcode'] = BaseFieldDefinition::create('language')
229 ->setLabel(t('Language code'))
230 ->setDescription(t('The node language code.'))
231 ->setRevisionable(TRUE);
233 $fields['entity_id'] = BaseFieldDefinition::create('integer')
234 ->setLabel(t('Entity ID'))
235 ->setDescription(t('ID of entity crop belongs to.'))
236 ->setSetting('unsigned', TRUE)
237 ->setRevisionable(TRUE)
240 $fields['entity_type'] = BaseFieldDefinition::create('string')
241 ->setLabel(t('Entity type'))
242 ->setDescription(t('The type of entity crop belongs to.'))
243 ->setRevisionable(TRUE)
246 // Denormalized information, which is calculated in storage plugin for a
247 // given entity type. Saved here for performance reasons in image effects.
249 // TODO - we are not enforcing uniqueness on this as we want to support more
250 // crops per same image/image_style combination. However, image effect
251 // operates with image URI only, which means we have no mechanism to
252 // distinguish between multiple crops in there. If we really want to
253 // support multiple crops we'll need to override core at least,
254 // in \Drupal\Core\Image\ImageFactory and \Drupal\Core\Image\Image.
255 // Let's leave this for now and simply load based on URI only.
256 // We can use some semi-smart approach in case there are multiple crops
257 // with same URI for now (first created, last created, ...).
258 $fields['uri'] = BaseFieldDefinition::create('uri')
260 ->setDescription(t('The URI of the image crop belongs to.'))
261 ->setRevisionable(TRUE)
262 ->setTranslatable(TRUE)
263 ->setSetting('max_length', 255);
265 $fields['height'] = BaseFieldDefinition::create('integer')
266 ->setLabel(t('Height'))
267 ->setDescription(t('The crop height.'))
268 ->setRevisionable(TRUE)
269 ->setTranslatable(TRUE)
271 ->setSetting('unsigned', TRUE);
273 $fields['width'] = BaseFieldDefinition::create('integer')
274 ->setLabel(t('Width'))
275 ->setDescription(t('The crop width.'))
276 ->setRevisionable(TRUE)
277 ->setTranslatable(TRUE)
279 ->setSetting('unsigned', TRUE);
281 $fields['x'] = BaseFieldDefinition::create('integer')
282 ->setLabel(t('X coordinate'))
283 ->setDescription(t("The crop's X coordinate."))
284 ->setRevisionable(TRUE)
285 ->setTranslatable(TRUE)
287 ->setSetting('unsigned', TRUE);
289 $fields['y'] = BaseFieldDefinition::create('integer')
290 ->setLabel(t('Y coordinate'))
291 ->setDescription(t("The crop's Y coordinate."))
292 ->setRevisionable(TRUE)
293 ->setTranslatable(TRUE)
295 ->setSetting('unsigned', TRUE);
297 $fields['revision_timestamp'] = BaseFieldDefinition::create('created')
298 ->setLabel(t('Revision timestamp'))
299 ->setDescription(t('The time that the current revision was created.'))
300 ->setQueryable(FALSE)
301 ->setRevisionable(TRUE);
303 $fields['revision_uid'] = BaseFieldDefinition::create('entity_reference')
304 ->setLabel(t('Revision author ID'))
305 ->setDescription(t('The user ID of the author of the current revision.'))
306 ->setSetting('target_type', 'user')
307 ->setQueryable(FALSE)
308 ->setRevisionable(TRUE);
310 $fields['revision_log'] = BaseFieldDefinition::create('string_long')
311 ->setLabel(t('Revision Log'))
312 ->setDescription(t('The log entry explaining the changes in this revision.'))
313 ->setRevisionable(TRUE)
314 ->setTranslatable(TRUE);