├── modules ├── cat_api │ ├── config │ │ ├── install │ │ │ └── cat_api.settings.yml │ │ └── schema │ │ │ └── cat_api.schema.yml │ ├── cat_api.info.yml │ ├── src │ │ ├── CredentialsInterface.php │ │ ├── Service │ │ │ ├── CatApiClientInterface.php │ │ │ ├── CatApiClient.php │ │ │ ├── CatBreedFactory.php │ │ │ └── CatApiClientFactory.php │ │ ├── Model │ │ │ ├── CatBreedInterface.php │ │ │ └── CatBreed.php │ │ ├── Form │ │ │ ├── SettingsForm.php │ │ │ └── SearchCatsForm.php │ │ ├── Plugin │ │ │ └── Block │ │ │ │ └── CatApiRandomCat.php │ │ └── Controller │ │ │ └── BrowseCatsPage.php │ ├── cat_api.services.yml │ ├── cat_api.routing.yml │ └── README.md ├── custom_entities │ ├── simple │ │ ├── simple.module │ │ ├── simple.permissions.yml │ │ ├── simple.info.yml │ │ ├── config │ │ │ └── simple.schema.yml │ │ ├── simple.links.action.yml │ │ ├── simple.links.menu.yml │ │ ├── simple.links.task.yml │ │ └── src │ │ │ ├── SimpleTypeListBuilder.php │ │ │ ├── SimpleListBuilder.php │ │ │ ├── Form │ │ │ ├── SimpleEntityForm.php │ │ │ └── SimpleTypeEntityForm.php │ │ │ └── Entity │ │ │ ├── SimpleEntity.php │ │ │ └── SimpleTypeEntity.php │ ├── practical │ │ ├── practical.module │ │ ├── practical.permissions.yml │ │ ├── practical.info.yml │ │ ├── config │ │ │ └── practical.schema.yml │ │ ├── practical.links.action.yml │ │ ├── src │ │ │ ├── Entity │ │ │ │ ├── PracticalTypeEntityInterface.php │ │ │ │ ├── PracticalEntityInterface.php │ │ │ │ ├── PracticalTypeEntity.php │ │ │ │ └── PracticalEntity.php │ │ │ ├── PracticalTypeListBuilder.php │ │ │ ├── Form │ │ │ │ ├── PracticalEntityForm.php │ │ │ │ └── PracticalTypeEntityForm.php │ │ │ ├── PracticalEntityAccessControlHandler.php │ │ │ ├── PracticalPermissionsGenerator.php │ │ │ └── PracticalListBuilder.php │ │ ├── practical.links.menu.yml │ │ └── practical.links.task.yml │ └── most_simple │ │ ├── most_simple.module │ │ ├── most_simple.permissions.yml │ │ ├── most_simple.info.yml │ │ ├── config │ │ └── most_simple.schema.yml │ │ └── src │ │ ├── Entity │ │ ├── MostSimpleEntity.php │ │ └── MostSimpleTypeEntity.php │ │ └── Form │ │ └── MostSimpleTypeEntityForm.php ├── blank_module │ └── blank_module.info.yml ├── cat_facts │ ├── README.md │ ├── cat_facts.services.yml │ ├── cat_facts.info.yml │ └── src │ │ ├── CatFactsClient.php │ │ └── Plugin │ │ └── Block │ │ └── CatFacts.php ├── custom_events │ ├── custom_events.info.yml │ ├── custom_events.module │ ├── src │ │ ├── Event │ │ │ └── UserLoginEvent.php │ │ └── EventSubscriber │ │ │ ├── ConfigEventsSubscriberWithDI.php │ │ │ ├── AnotherConfigEventsSubscriber.php │ │ │ └── UserLoginSubscriberWithDI.php │ └── custom_events.services.yml ├── calculator │ ├── calculator.info.yml │ ├── calculator.routing.yml │ ├── calculator.services.yml │ └── src │ │ ├── Service │ │ ├── CalculatorFactoryInterface.php │ │ ├── CalculatorFactory.php │ │ ├── CalculatorInterface.php │ │ └── Calculator.php │ │ ├── Model │ │ ├── CalculatedTotalInterface.php │ │ └── CalculatedTotal.php │ │ ├── Plugin │ │ └── Block │ │ │ └── MyGlobalCalculatorBlock.php │ │ └── Controller │ │ └── CalculatorSandbox.php ├── cookie_services │ ├── cookie_services.info.yml │ ├── cookie_services.routing.yml │ ├── src │ │ ├── Plugin │ │ │ └── Block │ │ │ │ └── BlockUsingCustomCacheContext.php │ │ ├── CookieServiceInterface.php │ │ ├── Cache │ │ │ └── Context │ │ │ │ └── CookieServiceComplexCacheContext.php │ │ ├── CookieServiceComplexData.php │ │ ├── CookieServiceSimple.php │ │ └── Form │ │ │ └── CookieServiceSandbox.php │ └── cookie_services.services.yml ├── services_examples │ ├── services_examples.info.yml │ ├── services_examples.routing.yml │ ├── src │ │ ├── PasswordGeneratorInterface.php │ │ ├── PasswordGeneratorCryptoSecure.php │ │ ├── PasswordGeneratorSimple.php │ │ ├── PasswordGeneratorUnambiguous.php │ │ ├── PasswordGeneratorUnambiguousDecoration.php │ │ ├── Controller │ │ │ └── PasswordGeneratorSandbox.php │ │ └── PasswordGeneratorAbstractBase.php │ └── services_examples.services.yml └── dependency_injection_examples │ ├── dependency_injection_examples.info.yml │ ├── dependency_injection_examples.routing.yml │ ├── src │ ├── AwesomeMarkupCreator.php │ ├── ServiceWithArguments.php │ ├── Form │ │ └── FormWithDependencyInjection.php │ ├── Plugin │ │ └── Block │ │ │ └── BlockWithDependencyInjection.php │ ├── Controller │ │ └── ControllerWithDependencyInjection.php │ ├── SetterInjectionExample.php │ └── ServiceWithWiredServices.php │ └── dependency_injection_examples.services.yml ├── themes └── blank_theme │ └── blank_theme.info.yml └── README.md /modules/cat_api/config/install/cat_api.settings.yml: -------------------------------------------------------------------------------- 1 | base_uri: 'https://api.thecatapi.com/v1/' 2 | api_key: '' 3 | -------------------------------------------------------------------------------- /modules/custom_entities/simple/simple.module: -------------------------------------------------------------------------------- 1 | dispatch($event, UserLoginEvent::EVENT_NAME); 20 | } 21 | -------------------------------------------------------------------------------- /modules/cat_api/cat_api.services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | # Client factory service. 3 | cat_api.client_factory: 4 | class: \Drupal\cat_api\Service\CatApiClientFactory 5 | arguments: 6 | - '@config.factory' 7 | - '@http_client_factory' 8 | - '@serialization.json' 9 | 10 | # Create a service from another Service Factory. 11 | cat_api.client: 12 | class: \Drupal\cat_api\Service\CatApiClient 13 | factory: ['@cat_api.client_factory', 'create'] 14 | 15 | # Cat Breed model factory. 16 | cat_api.breed_factory: 17 | class: \Drupal\cat_api\Service\CatBreedFactory 18 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/dependency_injection_examples.routing.yml: -------------------------------------------------------------------------------- 1 | controller_with_dependency_injection: 2 | path: 'dependency-injection-controller' 3 | defaults: 4 | _controller: \Drupal\dependency_injection_examples\Controller\ControllerWithDependencyInjection::page 5 | _title: 'Dependency Injection into Controller' 6 | requirements: 7 | _permission: 'access content' 8 | 9 | form_with_dependency_injection: 10 | path: 'dependency-injection-form' 11 | defaults: 12 | _form: \Drupal\dependency_injection_examples\Form\FormWithDependencyInjection 13 | _title: 'Dependency Injection into Form' 14 | requirements: 15 | _permission: 'access content' 16 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/src/AwesomeMarkupCreator.php: -------------------------------------------------------------------------------- 1 | {$value}"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /modules/calculator/src/Model/CalculatedTotalInterface.php: -------------------------------------------------------------------------------- 1 | arguments = func_get_args(); 24 | } 25 | 26 | /** 27 | * @return array 28 | * All arguments passed into the constructor. 29 | */ 30 | public function getArguments(): array { 31 | return $this->arguments; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /modules/services_examples/src/PasswordGeneratorCryptoSecure.php: -------------------------------------------------------------------------------- 1 | logger = $logger_channel_factory->get('pass_crypto'); 19 | } 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public function getRandomNumber(int $min = NULL, int $max = NULL) { 25 | return random_int($min, $max); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /modules/custom_events/src/Event/UserLoginEvent.php: -------------------------------------------------------------------------------- 1 | account = $account; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /modules/services_examples/src/PasswordGeneratorSimple.php: -------------------------------------------------------------------------------- 1 | t('Label'); 18 | $header['id'] = $this->t('Machine name'); 19 | 20 | return $header + parent::buildHeader(); 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function buildRow(EntityInterface $entity) { 27 | $row['label'] = $entity->label(); 28 | $row['id'] = $entity->id(); 29 | 30 | return $row + parent::buildRow($entity); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /modules/cat_api/README.md: -------------------------------------------------------------------------------- 1 | # Cat API module 2 | 3 | Custom Drupal module that integrates with [TheCatApi](https://thecatapi.com/). 4 | To use this module, you'll need to get an api key (free). 5 | 6 | This module shows most of the concepts and best practices discussed in my Drupal Camp Florida 2021 Training. 7 | 8 | * [Training Videos Playlist](https://youtube.com/playlist?list=PL8xTPkdstN3FOsuEJ_WibrF4xMelXRG4K) 9 | * [Training Slides](https://www.daggerhartlab.com/wp-content/uploads/Intermediate-OOP-in-Drupal-Jonathan-Daggerhart.pdf) 10 | 11 | ## Examples 12 | 13 | * [Object Design - Model](src/Model/CatBreed.php) 14 | * [Object Design - Service](src/Service/CatApiClient.php) 15 | * [Pattern - Simple Factory](src/Service/CatBreedFactory.php) 16 | * [Pattern - Method Factory](src/Controller/BrowseCatsPage.php) - Other method factories appear in the custom Form and Block plugin 17 | 18 | -------------------------------------------------------------------------------- /modules/services_examples/src/PasswordGeneratorUnambiguous.php: -------------------------------------------------------------------------------- 1 | logger = $logger_channel_factory->get('pass_unambig'); 19 | } 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public function getCharacterSets() { 25 | return [ 26 | 'numbers' => '23479', 27 | 'lower' => 'abcdefghjkmnpqrstuvwxyz', 28 | 'upper' => 'ACDEFHJKMNPQRTUVWXYZ', 29 | 'symbols' => '@#$%^&*(){}[]', 30 | ]; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /modules/cat_api/src/Model/CatBreedInterface.php: -------------------------------------------------------------------------------- 1 | t('Label'); 18 | $header['description'] = $this->t('Description'); 19 | $header['id'] = $this->t('Machine name'); 20 | 21 | return $header + parent::buildHeader(); 22 | } 23 | 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | public function buildRow(EntityInterface $entity) { 28 | /** @var \Drupal\practical\Entity\PracticalTypeEntityInterface $entity */ 29 | $row['label'] = $entity->label(); 30 | $row['description'] = $entity->getDescription(); 31 | $row['id'] = $entity->id(); 32 | 33 | return $row + parent::buildRow($entity); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/calculator/src/Model/CalculatedTotal.php: -------------------------------------------------------------------------------- 1 | update($initial_value); 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function value() { 33 | $last = array_key_last($this->history); 34 | return $this->history[$last]; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function update($next_value) { 41 | $this->history[] = $next_value; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function history(): array { 48 | return $this->history; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /modules/cat_facts/src/CatFactsClient.php: -------------------------------------------------------------------------------- 1 | client = $http_client_factory->fromOptions([ 26 | 'base_uri' => 'https://cat-fact.herokuapp.com/', 27 | ]); 28 | } 29 | 30 | /** 31 | * Get some random cat facts. 32 | * 33 | * @param int $amount 34 | * 35 | * @return array 36 | */ 37 | public function random($amount = 1) { 38 | $response = $this->client->get('facts/random', [ 39 | 'query' => [ 40 | 'amount' => $amount 41 | ] 42 | ]); 43 | 44 | $data = Json::decode($response->getBody()); 45 | 46 | if ($amount == 1) { 47 | $data = [$data]; 48 | } 49 | 50 | return $data; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /modules/custom_events/custom_events.services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | # Subscriber to the config events, with dependencies injected. 3 | # Name of this service. 4 | my_config_events_subscriber_with_di: 5 | # Event subscriber class that will listen for the events. 6 | class: '\Drupal\custom_events\EventSubscriber\ConfigEventsSubscriberWithDI' 7 | # Inject services as "arguments" 8 | arguments: 9 | - '@messenger' 10 | # Tagged as an event_subscriber to register this subscriber with the event_dispatch service. 11 | tags: 12 | - { name: 'event_subscriber' } 13 | 14 | # Some other event subscriber with dependency injection. 15 | another_config_events_subscriber: 16 | class: '\Drupal\custom_events\EventSubscriber\AnotherConfigEventsSubscriber' 17 | arguments: 18 | - '@messenger' 19 | tags: 20 | - { name: 'event_subscriber' } 21 | 22 | # Subscriber to the event we dispatch in hook_user_login, with dependencies injected. 23 | custom_events_user_login_with_di: 24 | class: '\Drupal\custom_events\EventSubscriber\UserLoginSubscriberWithDI' 25 | arguments: 26 | - '@database' 27 | - '@date.formatter' 28 | - '@messenger' 29 | tags: 30 | - { name: 'event_subscriber' } 31 | -------------------------------------------------------------------------------- /modules/custom_entities/simple/src/SimpleListBuilder.php: -------------------------------------------------------------------------------- 1 | t('Linked Entity Id'); 18 | $header['content_entity_label'] = $this->t('Content Entity Label'); 19 | $header['content_entity_id'] = $this->t('Content Entity Id'); 20 | $header['bundle_label'] = $this->t('Config Entity (Bundle) Label'); 21 | $header['bundle_id'] = $this->t('Config Entity (Bundle) Id'); 22 | 23 | return $header + parent::buildHeader(); 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function buildRow(EntityInterface $entity) { 30 | $row['id'] = $entity->toLink($entity->id()); 31 | $row['content_entity_label'] = $entity->getEntityType()->getLabel()->render(); 32 | $row['content_entity_id'] = $entity->getEntityType()->id(); 33 | $row['bundle_label'] = $entity->bundle->entity->label(); 34 | $row['bundle_id'] = $entity->bundle(); 35 | 36 | return $row + parent::buildRow($entity); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/cookie_services/src/Plugin/Block/BlockUsingCustomCacheContext.php: -------------------------------------------------------------------------------- 1 | addStatus('Block is not cached - ' . __CLASS__); 23 | 24 | return [ 25 | '#cache' => [ 26 | 'contexts' => [ 27 | 'another_cookie_service_complex_city' 28 | ], 29 | ], 30 | '#markup' => Markup::create(' 31 |

Note: Caching must be enabled to test this properly.

32 |
33 |

34 | Example custom cache context using cookie data.
35 | If this block not cached you should see a message indicating it is not cached.
36 | If it is cached, you should not see any message mentioning this blocks cached state. 37 |

38 | '), 39 | ]; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /modules/cat_api/src/Service/CatApiClient.php: -------------------------------------------------------------------------------- 1 | httpClient = $http_client; 37 | $this->json = $json; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | public function get(string $endpoint, array $query = []): array { 44 | $response = $this->httpClient->get($endpoint, [ 45 | 'query' => $query, 46 | ]); 47 | 48 | if ($response->getStatusCode() === 200) { 49 | return $this->json::decode($response->getBody()->getContents()); 50 | } 51 | 52 | return []; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /modules/custom_entities/simple/src/Form/SimpleEntityForm.php: -------------------------------------------------------------------------------- 1 | entity; 18 | $message_params = [ 19 | '%entity_label' => $entity->id(), 20 | '%content_entity_label' => $entity->getEntityType()->getLabel()->render(), 21 | '%bundle_label' => $entity->bundle->entity->label(), 22 | ]; 23 | 24 | $status = parent::save($form, $form_state); 25 | 26 | switch ($status) { 27 | case SAVED_NEW: 28 | $this->messenger()->addStatus($this->t('Created the %bundle_label - %content_entity_label entity: %entity_label.', $message_params )); 29 | break; 30 | 31 | default: 32 | $this->messenger()->addStatus($this->t('Saved the %bundle_label - %content_entity_label entity: %entity_label.', $message_params)); 33 | } 34 | 35 | $content_entity_id = $entity->getEntityType()->id(); 36 | $form_state->setRedirect("entity.{$content_entity_id}.canonical", [$content_entity_id => $entity->id()]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/cat_api/src/Service/CatBreedFactory.php: -------------------------------------------------------------------------------- 1 | entity; 18 | $message_params = [ 19 | '%entity_label' => $entity->id(), 20 | '%content_entity_label' => $entity->getEntityType()->getLabel()->render(), 21 | '%bundle_label' => $entity->bundle->entity->label(), 22 | ]; 23 | 24 | $status = parent::save($form, $form_state); 25 | 26 | switch ($status) { 27 | case SAVED_NEW: 28 | $this->messenger()->addStatus($this->t('Created the %bundle_label - %content_entity_label entity: %entity_label.', $message_params )); 29 | break; 30 | 31 | default: 32 | $this->messenger()->addStatus($this->t('Saved the %bundle_label - %content_entity_label entity: %entity_label.', $message_params)); 33 | } 34 | 35 | $content_entity_id = $entity->getEntityType()->id(); 36 | $form_state->setRedirect("entity.{$content_entity_id}.canonical", [$content_entity_id => $entity->id()]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/cookie_services/cookie_services.services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | # Basic example of cookie as a service. 4 | cookie_service_simple: 5 | class: \Drupal\cookie_services\CookieServiceSimple 6 | arguments: 7 | - '@request_stack' 8 | tags: 9 | - { name: 'event_subscriber' } 10 | 11 | # Cookie with more complex data. 12 | cookie_service_complex: 13 | class: \Drupal\cookie_services\CookieServiceComplexData 14 | arguments: 15 | - '@request_stack' 16 | - '@serialization.json' 17 | calls: 18 | # Calling setCookieName on construction allows us to reuse this class easily. 19 | - ['setCookieName', ['my_cookie_name']] 20 | tags: 21 | - { name: 'event_subscriber' } 22 | 23 | # Reuse our complex cookie class with a new cookie name. 24 | another_cookie_service_complex: 25 | class: \Drupal\cookie_services\CookieServiceComplexData 26 | arguments: 27 | - '@request_stack' 28 | - '@serialization.json' 29 | calls: 30 | - ['setCookieName', ['some_other_cookie']] 31 | tags: 32 | - { name: 'event_subscriber' } 33 | 34 | # Example custom cache context for our complex cookie data. 35 | cache_context.another_cookie_service_complex_city: 36 | class: \Drupal\cookie_services\Cache\Context\CookieServiceComplexCacheContext 37 | arguments: 38 | - '@another_cookie_service_complex' 39 | tags: 40 | - { name: cache_context } 41 | -------------------------------------------------------------------------------- /modules/custom_entities/practical/src/Entity/PracticalEntityInterface.php: -------------------------------------------------------------------------------- 1 | innerService = $password_generator_unambiguous; 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | public function generatePassword($length = 32, $allowed_sets = []) { 33 | $inner_service = get_class($this->innerService); 34 | \Drupal::messenger()->addStatus("This message provided by the decoration on {$inner_service}. Even though the original service was requested, the container smartly returned an instance of the decoration. If a service is decorated multiple times, the decoration with the highest priority going first."); 35 | 36 | return $this->innerService->generatePassword($length, $allowed_sets); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /modules/services_examples/services_examples.services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | # Very simple service that generates simple passwords. 4 | password_generator_simple: 5 | class: \Drupal\services_examples\PasswordGeneratorSimple 6 | 7 | ## -- Service Definition inheritance -- 8 | 9 | # Abstract password generator who's service definition should be inherited 10 | # by other services. 11 | password_generator_abstract_base: 12 | abstract: true 13 | class: \Drupal\services_examples\PasswordGeneratorAbstractBase 14 | arguments: 15 | - '@messenger' 16 | calls: 17 | - ['setLogger', ['@logger.factory'] ] 18 | 19 | # Better password generated with cryptographically secure random numbers. 20 | # Inherits 'arguments' and 'calls' from parent service definition. 21 | password_generator_crypto_secure: 22 | class: \Drupal\services_examples\PasswordGeneratorCryptoSecure 23 | parent: password_generator_abstract_base 24 | 25 | # Better password generator that only uses unambiguous characters. 26 | # Inherits 'arguments' and 'calls' from parent service definition. 27 | password_generator_unambiguous: 28 | class: \Drupal\services_examples\PasswordGeneratorUnambiguous 29 | parent: password_generator_abstract_base 30 | 31 | # Example decorating a service. 32 | password_generator_unambiguous_decoration: 33 | class: \Drupal\services_examples\PasswordGeneratorUnambiguousDecoration 34 | public: false 35 | decorates: password_generator_unambiguous 36 | arguments: 37 | - '@password_generator_unambiguous_decoration.inner' 38 | -------------------------------------------------------------------------------- /modules/cat_api/src/Form/SettingsForm.php: -------------------------------------------------------------------------------- 1 | config('cat_api.settings'); 36 | 37 | $form['base_uri'] = [ 38 | '#type' => 'textfield', 39 | '#title' => $this->t('Api Base Url'), 40 | '#default_value' => $config->get('base_uri'), 41 | ]; 42 | $form['api_key'] = [ 43 | '#type' => 'textfield', 44 | '#title' => $this->t('Api Key'), 45 | '#default_value' => $config->get('api_key'), 46 | ]; 47 | 48 | return parent::buildForm($form, $form_state); 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public function submitForm(array &$form, FormStateInterface $form_state) { 55 | $this->config('cat_api.settings') 56 | ->set('base_uri', rtrim($form_state->getValue('base_uri'), '/') . '/') 57 | ->set('api_key', trim($form_state->getValue('api_key'))) 58 | ->save(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/dependency_injection_examples.services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | # Very basic example of a service that we can use. 4 | awesome_markup_creator: 5 | class: \Drupal\dependency_injection_examples\AwesomeMarkupCreator 6 | 7 | # Impractical example to illustrate what arguments are. 8 | my_service_with_arguments: 9 | class: \Drupal\dependency_injection_examples\ServiceWithArguments 10 | arguments: 11 | - 'hello' 12 | - 17 13 | 14 | # Example to illustrate that the same class can be used with different arguments. 15 | my_other_service_with_arguments: 16 | class: \Drupal\dependency_injection_examples\ServiceWithArguments 17 | arguments: 18 | - 'another example' 19 | - 'with different arguments' 20 | 21 | # Practical example of injecting other services as dependencies into our new 22 | # service using arguments. 23 | # AKA: "Wiring" services, specifically "Manual Wiring". 24 | my_service_with_wired_services: 25 | class: \Drupal\dependency_injection_examples\ServiceWithWiredServices 26 | arguments: 27 | - '@awesome_markup_creator' 28 | - '@my_service_with_arguments' 29 | - '@messenger' 30 | - '@logger.factory' 31 | 32 | # Example of injecting dependencies using a Setter on the service class. 33 | my_service_with_setter_injection: 34 | class: \Drupal\dependency_injection_examples\SetterInjectionExample 35 | arguments: 36 | - '@my_other_service_with_arguments' 37 | calls: 38 | - ['setAwesomeMarkupCreator', ['@awesome_markup_creator'] ] 39 | - ['setLogger', ['@logger.factory'] ] 40 | - ['setMessenger', ['@messenger'] ] 41 | -------------------------------------------------------------------------------- /modules/cookie_services/src/CookieServiceInterface.php: -------------------------------------------------------------------------------- 1 | anotherCookieServiceComplex = $another_cookie_service_complex; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public static function getLabel() { 39 | return t('Another Cookie Service Complex - City'); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function getContext() { 46 | $cookie_value = $this->anotherCookieServiceComplex->getCookieValue(); 47 | return $cookie_value['city'] ?? false; 48 | } 49 | 50 | /** 51 | * {@inheritdoc} 52 | */ 53 | public function getCacheableMetadata() { 54 | return new CacheableMetadata(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /modules/custom_entities/simple/src/Entity/SimpleEntity.php: -------------------------------------------------------------------------------- 1 | catFactsClient = $cat_facts_client; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { 41 | return new static( 42 | $configuration, 43 | $plugin_id, 44 | $plugin_definition, 45 | $container->get('cat_facts_client') 46 | ); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function build() { 53 | $cat_facts = $this->catFactsClient->random(2); 54 | $items = []; 55 | 56 | foreach ($cat_facts as $cat_fact) { 57 | $items[] = $cat_fact['text']; 58 | } 59 | 60 | return [ 61 | '#theme' => 'item_list', 62 | '#items' => $items, 63 | ]; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /modules/custom_entities/most_simple/src/Entity/MostSimpleTypeEntity.php: -------------------------------------------------------------------------------- 1 | configFactory = $config_factory; 44 | $this->httpClientFactory = $http_client_factory; 45 | $this->json = $json; 46 | } 47 | 48 | /** 49 | * Create a new fully prepared instance of CatApiClient. 50 | * 51 | * @return \Drupal\cat_api\Service\CatApiClient 52 | */ 53 | public function create() { 54 | $config = $this->configFactory->get('cat_api.settings'); 55 | $http_client = $this->httpClientFactory->fromOptions([ 56 | 'verify' => FALSE, 57 | 'base_uri' => $config->get('base_uri'), 58 | 'headers' => [ 59 | 'x-api-key' => $config->get('api_key'), 60 | 'Content-Type' => 'application/json', 61 | ], 62 | ]); 63 | 64 | return new CatApiClient($http_client, $this->json); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /modules/calculator/src/Plugin/Block/MyGlobalCalculatorBlock.php: -------------------------------------------------------------------------------- 1 | get('global_calculator') 37 | ); 38 | } 39 | 40 | /** 41 | * MyGlobalCalculatorBlock constructor. 42 | * 43 | * @param array $configuration 44 | * @param $plugin_id 45 | * @param $plugin_definition 46 | * @param \Drupal\calculator\Service\CalculatorInterface $global_calculator 47 | */ 48 | public function __construct(array $configuration, $plugin_id, $plugin_definition, CalculatorInterface $global_calculator) { 49 | parent::__construct($configuration, $plugin_id, $plugin_definition); 50 | $this->globalCalculator = $global_calculator; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function build() { 57 | return [ 58 | '#markup' => Markup::create('Calculated Total: ' . $this->globalCalculator->getValue() ), 59 | ]; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /modules/calculator/src/Service/Calculator.php: -------------------------------------------------------------------------------- 1 | total = $starting_total; 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | function getValue() { 35 | return $this->total->value(); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function getTotal(): CalculatedTotalInterface { 42 | return $this->total; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | function add($a): CalculatorInterface { 49 | $this->total->update( 50 | $this->total->value() + $a 51 | ); 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | function subtract($a): CalculatorInterface { 60 | $this->total->update( 61 | $this->total->value() - $a 62 | ); 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | function divide($a): CalculatorInterface { 71 | $this->total->update( 72 | $this->total->value() / $a 73 | ); 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * {@inheritdoc} 80 | */ 81 | function multiply($a): CalculatorInterface { 82 | $this->total->update( 83 | $this->total->value() * $a 84 | ); 85 | 86 | return $this; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /modules/cat_api/src/Model/CatBreed.php: -------------------------------------------------------------------------------- 1 | id = $id; 55 | $this->name = $name; 56 | $this->description = $desc; 57 | $this->temperament = $temp; 58 | $this->imageUrl = $image_url; 59 | } 60 | 61 | /** 62 | * {@inheritDoc} 63 | */ 64 | public function getId(): string { 65 | return $this->id; 66 | } 67 | 68 | /** 69 | * {@inheritDoc} 70 | */ 71 | public function getName(): string { 72 | return $this->name; 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | */ 78 | public function getDescription(): string { 79 | return $this->description; 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | */ 85 | public function getTemperament(): string { 86 | return $this->temperament; 87 | } 88 | 89 | /** 90 | * {@inheritDoc} 91 | */ 92 | public function getPicture() { 93 | return Markup::create("{$this->name} picture"); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /modules/custom_entities/practical/src/PracticalEntityAccessControlHandler.php: -------------------------------------------------------------------------------- 1 | getEntityTypeId(); 19 | $bundle = $entity->bundle(); 20 | $is_owner = $entity->getOwnerId() === $account->id(); 21 | 22 | switch ($operation) { 23 | case 'view': 24 | if ($is_owner) { 25 | return AccessResult::allowedIfHasPermission($account, "view own $entity_type_id $bundle"); 26 | } 27 | return AccessResult::allowedIfHasPermission($account, "view any $entity_type_id $bundle"); 28 | 29 | case 'update': 30 | if ($is_owner) { 31 | return AccessResult::allowedIfHasPermission($account, "edit own $entity_type_id $bundle"); 32 | } 33 | return AccessResult::allowedIfHasPermission($account, "edit any $entity_type_id $bundle"); 34 | 35 | case 'delete': 36 | if ($is_owner) { 37 | return AccessResult::allowedIfHasPermission($account, "delete own $entity_type_id $bundle"); 38 | } 39 | return AccessResult::allowedIfHasPermission($account, "delete any $entity_type_id $bundle"); 40 | } 41 | 42 | // Unknown operation, no opinion. 43 | return AccessResult::neutral(); 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { 50 | return AccessResult::allowedIfHasPermission($account, "create {$context['entity_type_id']} $entity_bundle"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/src/Form/FormWithDependencyInjection.php: -------------------------------------------------------------------------------- 1 | get('my_service_with_wired_services') 30 | ); 31 | } 32 | 33 | /** 34 | * FormWithDependencyInjection constructor. 35 | * 36 | * @param \Drupal\dependency_injection_examples\ServiceWithWiredServices $service_with_wired_services 37 | */ 38 | public function __construct(ServiceWithWiredServices $service_with_wired_services) { 39 | $this->serviceWithWiredServices = $service_with_wired_services; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function getFormId() { 46 | return 'form_with_dependency_injection'; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function buildForm(array $form, FormStateInterface $form_state) { 53 | $form['submit'] = [ 54 | '#type' => 'submit', 55 | '#default_value' => $this->t('Submit'), 56 | ]; 57 | 58 | return $form; 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function submitForm(array &$form, FormStateInterface $form_state) { 65 | $this->serviceWithWiredServices->logServiceArgumentsAsMarkup(); 66 | $this->serviceWithWiredServices->messageServiceArgumentsAsMarkup(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/src/Plugin/Block/BlockWithDependencyInjection.php: -------------------------------------------------------------------------------- 1 | get('my_service_with_wired_services') 36 | ); 37 | } 38 | 39 | /** 40 | * BlockWithDependencyInjection constructor. 41 | * 42 | * @param array $configuration 43 | * @param $plugin_id 44 | * @param $plugin_definition 45 | * @param \Drupal\dependency_injection_examples\ServiceWithWiredServices $service_with_wired_services 46 | */ 47 | public function __construct(array $configuration, $plugin_id, $plugin_definition, ServiceWithWiredServices $service_with_wired_services) { 48 | parent::__construct($configuration, $plugin_id, $plugin_definition); 49 | $this->serviceWithWiredServices = $service_with_wired_services; 50 | } 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | public function build() { 56 | return [ 57 | '#markup' => $this->serviceWithWiredServices->getServiceArgumentsAsMarkup(), 58 | ]; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /modules/custom_entities/most_simple/src/Form/MostSimpleTypeEntityForm.php: -------------------------------------------------------------------------------- 1 | entity; 17 | $content_entity_id = $entity_type->getEntityType()->getBundleOf(); 18 | 19 | $form['label'] = [ 20 | '#type' => 'textfield', 21 | '#title' => $this->t('Label'), 22 | '#maxlength' => 255, 23 | '#default_value' => $entity_type->label(), 24 | '#description' => $this->t("Label for the %content_entity_id entity type (bundle).", ['%content_entity_id' => $content_entity_id]), 25 | '#required' => TRUE, 26 | ]; 27 | 28 | $form['id'] = [ 29 | '#type' => 'machine_name', 30 | '#default_value' => $entity_type->id(), 31 | '#machine_name' => [ 32 | 'exists' => '\Drupal\mostSimple\Entity\MostSimpleTypeEntity::load', 33 | ], 34 | '#disabled' => !$entity_type->isNew(), 35 | ]; 36 | 37 | return $this->protectBundleIdElement($form); 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public function save(array $form, FormStateInterface $form_state) { 44 | $entity_type = $this->entity; 45 | $status = $entity_type->save(); 46 | $message_params = [ 47 | '%label' => $entity_type->label(), 48 | '%content_entity_id' => $entity_type->getEntityType()->getBundleOf(), 49 | ]; 50 | 51 | switch ($status) { 52 | case SAVED_NEW: 53 | $this->messenger()->addStatus($this->t('Created the %label %content_entity_id entity type.', $message_params)); 54 | break; 55 | 56 | default: 57 | $this->messenger()->addStatus($this->t('Saved the %label %content_entity_id entity type.', $message_params)); 58 | } 59 | 60 | $form_state->setRedirectUrl($entity_type->toUrl('collection')); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /modules/custom_events/src/EventSubscriber/ConfigEventsSubscriberWithDI.php: -------------------------------------------------------------------------------- 1 | messenger = $messenger; 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | * 37 | * @return array 38 | * The event names to listen for, and the methods that should be executed. 39 | */ 40 | public static function getSubscribedEvents() { 41 | return [ 42 | ConfigEvents::SAVE => 'configSave', 43 | ConfigEvents::DELETE => 'configDelete', 44 | ]; 45 | } 46 | 47 | /** 48 | * React to a config object being saved. 49 | * 50 | * @param \Drupal\Core\Config\ConfigCrudEvent $event 51 | * Config crud event. 52 | */ 53 | public function configSave(ConfigCrudEvent $event) { 54 | $config = $event->getConfig(); 55 | $this->messenger->addStatus(__CLASS__ . ' - Saved config: ' . $config->getName()); 56 | } 57 | 58 | /** 59 | * React to a config object being deleted. 60 | * 61 | * @param \Drupal\Core\Config\ConfigCrudEvent $event 62 | * Config crud event. 63 | */ 64 | public function configDelete(ConfigCrudEvent $event) { 65 | $config = $event->getConfig(); 66 | $this->messenger->addStatus(__CLASS__ . ' - Deleted config: ' . $config->getName()); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /modules/custom_events/src/EventSubscriber/AnotherConfigEventsSubscriber.php: -------------------------------------------------------------------------------- 1 | messenger = $messenger; 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | * 37 | * @return array 38 | * The event names to listen for, and the methods that should be executed. 39 | */ 40 | public static function getSubscribedEvents() { 41 | return [ 42 | ConfigEvents::SAVE => ['configSave', 100], 43 | ConfigEvents::DELETE => ['configDelete', -100], 44 | ]; 45 | } 46 | 47 | /** 48 | * React to a config object being saved. 49 | * 50 | * @param \Drupal\Core\Config\ConfigCrudEvent $event 51 | * Config crud event. 52 | */ 53 | public function configSave(ConfigCrudEvent $event) { 54 | $config = $event->getConfig(); 55 | $this->messenger->addStatus(__CLASS__ . ' - Saved config: ' . $config->getName()); 56 | } 57 | 58 | /** 59 | * React to a config object being deleted. 60 | * 61 | * @param \Drupal\Core\Config\ConfigCrudEvent $event 62 | * Config crud event. 63 | */ 64 | public function configDelete(ConfigCrudEvent $event) { 65 | $config = $event->getConfig(); 66 | $this->messenger->addStatus(__CLASS__ . ' - Deleted config: ' . $config->getName()); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /modules/custom_entities/practical/src/PracticalPermissionsGenerator.php: -------------------------------------------------------------------------------- 1 | buildPermissions($entity_type); 24 | } 25 | return $perms; 26 | } 27 | 28 | /** 29 | * Create the permissions desired for an individual entity type. 30 | * 31 | * @param PracticalTypeEntity $entity_type 32 | * 33 | * @return array 34 | */ 35 | protected function buildPermissions(PracticalTypeEntity $entity_type) { 36 | $type_id = $entity_type->id(); 37 | $bundle_of = $entity_type->getEntityType()->getBundleOf(); 38 | $type_params = [ 39 | '%type_name' => $entity_type->label(), 40 | '%bundle_of' => $bundle_of, 41 | ]; 42 | 43 | return [ 44 | "create $bundle_of $type_id" => [ 45 | 'title' => $this->t('%type_name: Create new %bundle_of', $type_params), 46 | ], 47 | "view any $bundle_of $type_id" => [ 48 | 'title' => $this->t('%type_name: View any %bundle_of', $type_params), 49 | ], 50 | "view own $bundle_of $type_id" => [ 51 | 'title' => $this->t('%type_name: View own %bundle_of', $type_params), 52 | ], 53 | "edit any $bundle_of $type_id" => [ 54 | 'title' => $this->t('%type_name: Edit any %bundle_of', $type_params), 55 | ], 56 | "edit own $bundle_of $type_id" => [ 57 | 'title' => $this->t('%type_name: Edit own %bundle_of', $type_params), 58 | ], 59 | "delete any $bundle_of $type_id" => [ 60 | 'title' => $this->t('%type_name: Delete any %bundle_of', $type_params), 61 | ], 62 | "delete own $bundle_of $type_id" => [ 63 | 'title' => $this->t('%type_name: Delete own %bundle_of', $type_params), 64 | ], 65 | ]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/src/Controller/ControllerWithDependencyInjection.php: -------------------------------------------------------------------------------- 1 | get('my_service_with_wired_services'), 37 | $container->get('my_service_with_setter_injection') 38 | ); 39 | } 40 | 41 | /** 42 | * ControllerWithDependencyInjection constructor. 43 | * 44 | * @param \Drupal\dependency_injection_examples\ServiceWithWiredServices $service_with_wired_services 45 | * @param \Drupal\dependency_injection_examples\SetterInjectionExample $setter_injection_example 46 | */ 47 | public function __construct(ServiceWithWiredServices $service_with_wired_services, SetterInjectionExample $setter_injection_example) { 48 | $this->serviceWithWiredServices = $service_with_wired_services; 49 | $this->setterInjectionExample = $setter_injection_example; 50 | } 51 | 52 | /** 53 | * Page output. 54 | */ 55 | public function page() { 56 | $this->serviceWithWiredServices->messageServiceArgumentsAsMarkup(); 57 | $this->setterInjectionExample->messageServiceArgumentsAsMarkup(); 58 | 59 | return [ 60 | '#markup' => $this->serviceWithWiredServices->getServiceArgumentsAsMarkup(), 61 | ]; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /modules/cat_api/src/Plugin/Block/CatApiRandomCat.php: -------------------------------------------------------------------------------- 1 | catApiClient = $cat_api_client; 50 | $this->catBreedFactory = $cat_breed_factory; 51 | 52 | } 53 | 54 | /** 55 | * {@inheritDoc} 56 | */ 57 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { 58 | return new static( 59 | $configuration, 60 | $plugin_id, 61 | $plugin_definition, 62 | $container->get('cat_api.client'), 63 | $container->get('cat_api.breed_factory') 64 | ); 65 | } 66 | 67 | /** 68 | * {@inheritDoc} 69 | */ 70 | public function build() { 71 | $breeds = $this->catApiClient->get('breeds'); 72 | shuffle($breeds); 73 | 74 | $cat_breed = $this->catBreedFactory->createFromBreedResult($breeds[0]); 75 | 76 | return [ 77 | 'image' => [ 78 | '#markup' => $cat_breed->getPicture(), 79 | ], 80 | ]; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /modules/custom_events/src/EventSubscriber/UserLoginSubscriberWithDI.php: -------------------------------------------------------------------------------- 1 | database = $database; 49 | $this->dateFormatter = $date_formatter; 50 | $this->messenger = $messenger; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public static function getSubscribedEvents() { 57 | return [ 58 | // Static class constant => method on this class. 59 | UserLoginEvent::EVENT_NAME => 'onUserLogin', 60 | ]; 61 | } 62 | 63 | /** 64 | * React to the user login event dispatched. 65 | * 66 | * @param \Drupal\custom_events\Event\UserLoginEvent $event 67 | * Dat event object yo. 68 | */ 69 | public function onUserLogin(UserLoginEvent $event) { 70 | $account_created = $this->database->select('users_field_data', 'ud') 71 | ->fields('ud', ['created']) 72 | ->condition('ud.uid', $event->account->id()) 73 | ->execute() 74 | ->fetchField(); 75 | 76 | $this->messenger->addStatus(t(__CLASS__ . ' - Welcome, your account was created on %created_date.', [ 77 | '%created_date' => $this->dateFormatter->format($account_created, 'short'), 78 | ])); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /modules/custom_entities/practical/src/Entity/PracticalTypeEntity.php: -------------------------------------------------------------------------------- 1 | description; 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | public function setDescription($description) { 83 | $this->description = $description; 84 | return $this; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /modules/calculator/src/Controller/CalculatorSandbox.php: -------------------------------------------------------------------------------- 1 | get('messenger'), 42 | $container->get('calculator_factory'), 43 | $container->get('global_calculator') 44 | ); 45 | } 46 | 47 | /** 48 | * CalculatorSandbox constructor. 49 | * 50 | * @param \Drupal\Core\Messenger\MessengerInterface $messenger 51 | * Core messenger instance. 52 | * @param \Drupal\calculator\Service\CalculatorFactoryInterface $calculator_factory 53 | * Calculator factory. 54 | * @param \Drupal\calculator\Service\CalculatorInterface $global_calculator 55 | * Global calculator. 56 | */ 57 | public function __construct( 58 | MessengerInterface $messenger, 59 | CalculatorFactoryInterface $calculator_factory, 60 | CalculatorInterface $global_calculator 61 | ) 62 | { 63 | $this->messenger = $messenger; 64 | $this->calculatorFactory = $calculator_factory; 65 | $this->globalCalculator = $global_calculator; 66 | } 67 | 68 | /** 69 | * @return array 70 | * Render array. 71 | */ 72 | public function page() { 73 | $this->messenger->addStatus('Hello'); 74 | $this->globalCalculator 75 | ->add(5) 76 | ->subtract(7) 77 | ->add(156.89) 78 | ->divide(3) 79 | ; 80 | 81 | return [ 82 | 'total' => [ 83 | '#markup' => $this->globalCalculator->getValue(), 84 | ], 85 | 'history' => [ 86 | '#markup' => Markup::create('
' . print_r($this->globalCalculator->getTotal()->history(),1) . '
'), 87 | ] 88 | ]; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/src/SetterInjectionExample.php: -------------------------------------------------------------------------------- 1 | serviceWithArguments = $service_with_arguments; 45 | } 46 | 47 | /** 48 | * Set the service's awesome markup creator. 49 | * 50 | * @param \Drupal\dependency_injection_examples\AwesomeMarkupCreator $awesome_markup_creator 51 | */ 52 | public function setAwesomeMarkupCreator(AwesomeMarkupCreator $awesome_markup_creator) { 53 | $this->awesomeMarkupCreator = $awesome_markup_creator; 54 | } 55 | 56 | /** 57 | * Set the service's logger. 58 | * 59 | * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_channel_factory 60 | */ 61 | public function setLogger(LoggerChannelFactoryInterface $logger_channel_factory) { 62 | $this->logger = $logger_channel_factory->get('di_injected'); 63 | } 64 | 65 | /** 66 | * Set the service's messenger. 67 | * 68 | * @param \Drupal\Core\Messenger\MessengerInterface $messenger 69 | */ 70 | public function setMessenger(MessengerInterface $messenger) { 71 | $this->messenger = $messenger; 72 | } 73 | 74 | /** 75 | * Use the messenger service to output the arguments from our service. 76 | */ 77 | public function messageServiceArgumentsAsMarkup() { 78 | $this->messenger->addStatus('Message and arguments using our injected services:'); 79 | $arguments = $this->serviceWithArguments->getArguments(); 80 | $message = $this->awesomeMarkupCreator->makeMarkup($arguments); 81 | $this->messenger->addStatus($message); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Custom Drupal 8 Entities w/ Bundle Examples 2 | 3 | 1. [Most Simple](modules/custom_entities/most_simple) - The Most Simple entity with bundles possible. Easy to create (very little code), hard to use (no links). 4 | 1. [Simple](modules/custom_entities/simple) - Simple Entity with bundles that is much more usable than the Most Simple Entity. Has links, useful ListBuilders, and informative messages. 5 | 1. [Practical](modules/custom_entities/practical) - More practical entity with bundles, generated bundle permissions, custom access control, entity fields, bundle descriptions, and much better ListBuilders with useful information. Also has interfaces for the Entity classes as best practice. 6 | 7 | [Related Blog Post](http://www.daggerhart.com/drupal-8-custom-entities-bundles/) 8 | 9 | # Learning about Drupal 8 Events 10 | 11 | 1. [Module](modules/custom_events) 12 | 1. [Registering services](modules/custom_events/custom_events.services.yml) - Registering services 13 | 1. [Config CRUD Event Subscriber](modules/custom_events/src/EventSubscriber/ConfigEventsSubscriberWithDI.php) - Example event subscriber that listens for Config object events. 14 | 1. [Custom Event - UserLoginEvent](modules/custom_events/src/Event/UserLoginEvent.php) - Custom Event that will be dispatched on `hook_user_login()`. 15 | 1. [Custom Event - Dispatching Events](modules/custom_events/custom_events.module) - Dispatching an event during `hook_user_login()`. 16 | 1. [Custom Event - Subscribing to Custom Event](modules/custom_events/src/EventSubscriber/ConfigEventsSubscriberWithDI.php) - Event Subscriber that listens for our custom UserLoginEvent. 17 | 18 | [Related Blog Post](https://www.daggerhart.com/drupal-8-hooks-events-event-subscribers/) 19 | 20 | # Services & Dependency Injection 21 | 22 | 1. [Dependency Injection Examples](modules/dependency_injection_examples) 23 | 1. [Services Examples](modules/services_examples) 24 | 1. [Calculator](modules/calculator) - More examples of services and dependency injection. 25 | 26 | # The most bare-minimum possible Drupal 8 module 27 | 28 | 1. [Module](modules/blank_module) 29 | 30 | # The most bare-minimum possible Drupal 8 theme 31 | 32 | 1. [Theme](themes/blank_theme) - This can be enabled from the modules folder. 33 | 34 | # Cat API custom modules 35 | 36 | These modules integrate with various free cat related APIs available online. 37 | 38 | 1. [Cat Facts](modules/cat_facts) 39 | 1. [Original Blog Post](https://www.hook42.com/blog/consuming-json-apis-drupal-8) 40 | 1. [Related Blog Post](https://www.daggerhart.com/guzzle-requests-json-in-drupal-8/) 41 | 1. [Cat API](modules/cat_api) 42 | * Services 43 | * Blocks 44 | * Dependency Injection 45 | -------------------------------------------------------------------------------- /modules/dependency_injection_examples/src/ServiceWithWiredServices.php: -------------------------------------------------------------------------------- 1 | awesomeMarkupCreator = $awesome_markup_creator; 59 | $this->serviceWithArguments = $service_with_arguments; 60 | $this->messenger = $messenger; 61 | $this->logger = $logger_channel_factory->get('di_wired'); 62 | } 63 | 64 | /** 65 | * Get the arguments from our custom service with arguments as markup. 66 | * 67 | * @return \Drupal\Component\Render\MarkupInterface|string 68 | */ 69 | public function getServiceArgumentsAsMarkup() { 70 | $arguments = $this->serviceWithArguments->getArguments(); 71 | return $this->awesomeMarkupCreator->makeMarkup($arguments); 72 | } 73 | 74 | /** 75 | * Use the logger to create a log of the arguments from our service. 76 | */ 77 | public function logServiceArgumentsAsMarkup() { 78 | $this->logger->info('Arguments from our service: %arguments_markup', [ 79 | '%arguments_markup' => $this->getServiceArgumentsAsMarkup(), 80 | ]); 81 | } 82 | 83 | /** 84 | * Use the messenger service to output the arguments from our service. 85 | */ 86 | public function messageServiceArgumentsAsMarkup() { 87 | $this->messenger->addStatus('Arguments from our service:'); 88 | $this->messenger->addStatus( 89 | $this->getServiceArgumentsAsMarkup() 90 | ); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /modules/custom_entities/practical/src/PracticalListBuilder.php: -------------------------------------------------------------------------------- 1 | dateFormatter = $date_formatter; 48 | $this->renderer = $renderer; 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { 55 | return new static( 56 | $entity_type, 57 | $container->get('entity_type.manager')->getStorage($entity_type->id()), 58 | $container->get('date.formatter'), 59 | $container->get('renderer') 60 | ); 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function buildHeader(){ 67 | $header['id'] = $this->t('Linked Entity Label'); 68 | $header['bundle_id'] = $this->t('Bundle'); 69 | $header['owner'] = $this->t('Owner'); 70 | $header['created'] = $this->t('Created'); 71 | $header['changed'] = $this->t('Changed'); 72 | 73 | return $header + parent::buildHeader(); 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | */ 79 | public function buildRow(EntityInterface $entity) { 80 | /** @var \Drupal\practical\Entity\PracticalEntityInterface $entity */ 81 | $row['id'] = $entity->toLink($entity->label()); 82 | $row['bundle_id'] = $entity->bundle(); 83 | $row['owner'] = $entity->getOwner()->toLink($entity->getOwner()->label()); 84 | $row['created'] = $this->dateFormatter->format($entity->getCreatedTime(), 'short'); 85 | $row['changed'] = $this->dateFormatter->format($entity->getChangedTime(), 'short'); 86 | 87 | return $row + parent::buildRow($entity); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /modules/custom_entities/simple/src/Form/SimpleTypeEntityForm.php: -------------------------------------------------------------------------------- 1 | entity; 18 | $content_entity_id = $entity_type->getEntityType()->getBundleOf(); 19 | 20 | $form['label'] = [ 21 | '#type' => 'textfield', 22 | '#title' => $this->t('Label'), 23 | '#maxlength' => 255, 24 | '#default_value' => $entity_type->label(), 25 | '#description' => $this->t("Label for the %content_entity_id entity type (bundle).", ['%content_entity_id' => $content_entity_id]), 26 | '#required' => TRUE, 27 | ]; 28 | 29 | $form['id'] = [ 30 | '#type' => 'machine_name', 31 | '#default_value' => $entity_type->id(), 32 | '#machine_name' => [ 33 | 'exists' => '\Drupal\simple\Entity\SimpleTypeEntity::load', 34 | ], 35 | '#disabled' => !$entity_type->isNew(), 36 | ]; 37 | 38 | return $this->protectBundleIdElement($form); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | protected function actions(array $form, FormStateInterface $form_state) { 45 | $actions = parent::actions($form, $form_state); 46 | 47 | if (\Drupal::moduleHandler()->moduleExists('field_ui') && $this->getEntity()->isNew()) { 48 | $actions['save_continue'] = $actions['submit']; 49 | $actions['save_continue']['#value'] = $this->t('Save and manage fields'); 50 | $actions['save_continue']['#submit'][] = [$this, 'redirectToFieldUi']; 51 | } 52 | 53 | return $actions; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function save(array $form, FormStateInterface $form_state) { 60 | $entity_type = $this->entity; 61 | $status = $entity_type->save(); 62 | $message_params = [ 63 | '%label' => $entity_type->label(), 64 | '%content_entity_id' => $entity_type->getEntityType()->getBundleOf(), 65 | ]; 66 | 67 | switch ($status) { 68 | case SAVED_NEW: 69 | $this->messenger()->addStatus($this->t('Created the %label %content_entity_id entity type.', $message_params)); 70 | break; 71 | 72 | default: 73 | $this->messenger()->addStatus($this->t('Saved the %label %content_entity_id entity type.', $message_params)); 74 | } 75 | 76 | $form_state->setRedirectUrl($entity_type->toUrl('collection')); 77 | } 78 | 79 | /** 80 | * Form submission handler to redirect to Manage fields page of Field UI. 81 | * 82 | * @param array $form 83 | * @param FormStateInterface $form_state 84 | */ 85 | public function redirectToFieldUi(array $form, FormStateInterface $form_state) { 86 | $route_info = FieldUI::getOverviewRouteInfo($this->entity->getEntityType()->getBundleOf(), $this->entity->id()); 87 | 88 | if ($form_state->getTriggeringElement()['#parents'][0] === 'save_continue' && $route_info) { 89 | $form_state->setRedirectUrl($route_info); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /modules/services_examples/src/Controller/PasswordGeneratorSandbox.php: -------------------------------------------------------------------------------- 1 | get('password_generator_simple'), 39 | $container->get('password_generator_crypto_secure'), 40 | $container->get('password_generator_unambiguous') 41 | ); 42 | } 43 | 44 | /** 45 | * PasswordGeneratorSandbox constructor. 46 | * 47 | * @param \Drupal\services_examples\PasswordGeneratorSimple $password_generator_simple 48 | * Simple password generator service. 49 | * @param \Drupal\services_examples\PasswordGeneratorInterface $password_generator_crypto_secure 50 | * Crypto secure password generator service. 51 | * @param \Drupal\services_examples\PasswordGeneratorInterface $password_generator_unambiguous_crypto_secure 52 | * Unambiguous password generator service (decorated) 53 | */ 54 | public function __construct( 55 | PasswordGeneratorSimple $password_generator_simple, 56 | PasswordGeneratorInterface $password_generator_crypto_secure, 57 | PasswordGeneratorInterface $password_generator_unambiguous_crypto_secure 58 | ) { 59 | $this->simple = $password_generator_simple; 60 | $this->crypto = $password_generator_crypto_secure; 61 | $this->unambiguous = $password_generator_unambiguous_crypto_secure; 62 | } 63 | 64 | /** 65 | * Page output. 66 | * 67 | * @return array[] 68 | * Render array. 69 | */ 70 | public function page() { 71 | return [ 72 | 'password' => [ 73 | '#markup' => Markup::create(" 74 |

Basic Password

75 |
{$this->simple->generatePassword()}
76 | "), 77 | ], 78 | 'crypto' => [ 79 | '#markup' => Markup::create(" 80 |

Cryptographically Secure Password

81 |
{$this->crypto->generatePassword()}
82 | "), 83 | ], 84 | 'unambiguous' => [ 85 | '#markup' => Markup::create(" 86 |

Unambiguous Password

87 |
{$this->unambiguous->generatePassword()}
88 | "), 89 | ], 90 | 'unambiguous_limited' => [ 91 | '#markup' => Markup::create(" 92 |

Unambiguous Password limited

93 |
{$this->unambiguous->generatePassword(32, ['numbers', 'lower', 'upper'])}
94 | "), 95 | ], 96 | ]; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /modules/services_examples/src/PasswordGeneratorAbstractBase.php: -------------------------------------------------------------------------------- 1 | messenger = $messenger; 40 | } 41 | 42 | /** 43 | * Set the logger channel instance on this service. 44 | * 45 | * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_channel_factory 46 | * Logger factory. 47 | */ 48 | abstract public function setLogger(LoggerChannelFactoryInterface $logger_channel_factory); 49 | 50 | /** 51 | * Get a random number. 52 | * 53 | * @param int|null $min 54 | * Number floor. 55 | * @param int|null $max 56 | * Number ceiling. 57 | * 58 | * @return int 59 | */ 60 | abstract public function getRandomNumber(int $min = 0, int $max = NULL); 61 | 62 | /** 63 | * Return an associative array where each value is a list of characters that 64 | * can be used for password generation. 65 | * 66 | * @return array 67 | */ 68 | public function getCharacterSets() { 69 | return [ 70 | 'numbers' => '0123456789', 71 | 'lower' => 'abcdefghijklmnopqrstuvwxyz', 72 | 'upper' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 73 | 'symbols' => '@#$%^&*(){}[]', 74 | ]; 75 | } 76 | 77 | /** 78 | * {@inheritDoc} 79 | */ 80 | public function generatePassword($length = 16, $allowed_sets = []) { 81 | $sets = $this->getCharacterSets(); 82 | 83 | if (!empty($allowed_sets)) { 84 | $sets = array_filter($sets, function($key) use ($allowed_sets) { 85 | return in_array($key, $allowed_sets); 86 | }, ARRAY_FILTER_USE_KEY); 87 | } 88 | 89 | $total_sets = count($sets); 90 | $password = ''; 91 | // Ensure each set is used at least once. 92 | foreach ($sets as $characters) { 93 | $characters_length = strlen($characters); 94 | $password .= $characters[$this->getRandomNumber(0, $characters_length - 1)]; 95 | $length -= 1; 96 | } 97 | 98 | for ($i = 0; $i < $length; $i++) { 99 | // Get a random character set. 100 | $characters = array_values($sets)[$this->getRandomNumber(0, $total_sets -1)]; 101 | $characters_length = strlen($characters); 102 | 103 | // Get a random character from the set. 104 | $password .= $characters[$this->getRandomNumber(0, $characters_length - 1)]; 105 | } 106 | 107 | $this->messenger->addStatus($this->t('Password Generated: %password', [ 108 | '%password' => $password, 109 | ])); 110 | 111 | $this->logger->info('Password Generated. Length: %length, Sets: %sets', [ 112 | '%length' => $length + $total_sets, 113 | '%sets' => implode(', ', array_keys($sets)), 114 | ]); 115 | 116 | return $password; 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /modules/custom_entities/practical/src/Form/PracticalTypeEntityForm.php: -------------------------------------------------------------------------------- 1 | entity; 19 | $content_entity_id = $entity_type->getEntityType()->getBundleOf(); 20 | 21 | $form['label'] = [ 22 | '#type' => 'textfield', 23 | '#title' => $this->t('Label'), 24 | '#maxlength' => 255, 25 | '#default_value' => $entity_type->label(), 26 | '#description' => $this->t("Label for the %content_entity_id entity type (bundle).", ['%content_entity_id' => $content_entity_id]), 27 | '#required' => TRUE, 28 | ]; 29 | 30 | $form['id'] = [ 31 | '#type' => 'machine_name', 32 | '#default_value' => $entity_type->id(), 33 | '#machine_name' => [ 34 | 'exists' => '\Drupal\practical\Entity\PracticalTypeEntity::load', 35 | ], 36 | '#disabled' => !$entity_type->isNew(), 37 | ]; 38 | 39 | $form['description'] = [ 40 | '#title' => $this->t('Description'), 41 | '#type' => 'textarea', 42 | '#default_value' => $entity_type->getDescription(), 43 | '#description' => $this->t('This text will be displayed on the "Add %content_entity_id" page.', ['%content_entity_id' => $content_entity_id]), 44 | ]; 45 | 46 | return $this->protectBundleIdElement($form); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | protected function actions(array $form, FormStateInterface $form_state) { 53 | $actions = parent::actions($form, $form_state); 54 | 55 | if (\Drupal::moduleHandler()->moduleExists('field_ui') && $this->getEntity()->isNew()) { 56 | $actions['save_continue'] = $actions['submit']; 57 | $actions['save_continue']['#value'] = $this->t('Save and manage fields'); 58 | $actions['save_continue']['#submit'][] = [$this, 'redirectToFieldUi']; 59 | } 60 | 61 | return $actions; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function save(array $form, FormStateInterface $form_state) { 68 | $entity_type = $this->entity; 69 | $status = $entity_type->save(); 70 | $message_params = [ 71 | '%label' => $entity_type->label(), 72 | '%content_entity_id' => $entity_type->getEntityType()->getBundleOf(), 73 | ]; 74 | 75 | switch ($status) { 76 | case SAVED_NEW: 77 | $this->messenger()->addStatus($this->t('Created the %label %content_entity_id entity type.', $message_params)); 78 | break; 79 | 80 | default: 81 | $this->messenger()->addStatus($this->t('Saved the %label %content_entity_id entity type.', $message_params)); 82 | } 83 | 84 | $form_state->setRedirectUrl($entity_type->toUrl('collection')); 85 | } 86 | 87 | /** 88 | * Form submission handler to redirect to Manage fields page of Field UI. 89 | * 90 | * @param array $form 91 | * @param FormStateInterface $form_state 92 | */ 93 | public function redirectToFieldUi(array $form, FormStateInterface $form_state) { 94 | $route_info = FieldUI::getOverviewRouteInfo($this->entity->getEntityType()->getBundleOf(), $this->entity->id()); 95 | 96 | if ($form_state->getTriggeringElement()['#parents'][0] === 'save_continue' && $route_info) { 97 | $form_state->setRedirectUrl($route_info); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /modules/cat_api/src/Controller/BrowseCatsPage.php: -------------------------------------------------------------------------------- 1 | catApiClient = $cat_api_client; 52 | $this->catBreedFactory = $cat_breed_factory; 53 | $this->request = $request_stack->getCurrentRequest(); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | public static function create(ContainerInterface $container) { 60 | return new static( 61 | $container->get('cat_api.client'), 62 | $container->get('cat_api.breed_factory'), 63 | $container->get('request_stack') 64 | ); 65 | } 66 | 67 | /** 68 | * Handle the cat browser page route. 69 | * 70 | * @param null $breed_id 71 | * 72 | * @return array 73 | */ 74 | public function page($breed_id = NULL) { 75 | if (empty($breed_id)) { 76 | return $this->getBreedLinks(); 77 | } 78 | 79 | return [ 80 | 'back' => [ 81 | '#markup' => Link::createFromRoute('Back', 'cat_api.browse_cats_page')->toString(), 82 | ], 83 | 'details' => $this->getBreedDetails($breed_id), 84 | ]; 85 | } 86 | 87 | /** 88 | * Get a list of links for all breeds. 89 | * 90 | * @return array 91 | */ 92 | private function getBreedLinks() { 93 | $breeds = $this->catApiClient->get('breeds'); 94 | $links = []; 95 | foreach ($breeds as $breed) { 96 | $links[] = Link::createFromRoute($breed['name'], 'cat_api.browse_cats_page', ['breed_id' => $breed['id']]); 97 | } 98 | return [ 99 | '#theme' => 'item_list', 100 | '#items' => $links, 101 | ]; 102 | } 103 | 104 | /** 105 | * Get details about a specific cat breed. 106 | * 107 | * @param string $breed_id 108 | * 109 | * @return array[] 110 | */ 111 | private function getBreedDetails(string $breed_id) { 112 | $results = $this->catApiClient->get('images/search', [ 113 | 'breed_ids' => $breed_id, 114 | ]); 115 | 116 | $cat_breed = $this->catBreedFactory->createFromSearchResult($results[0]); 117 | 118 | return [ 119 | 'name' => [ 120 | '#type' => 'html_tag', 121 | '#tag' => 'h2', 122 | '#value' => $cat_breed->getName(), 123 | ], 124 | 'details' => [ 125 | '#theme' => 'item_list', 126 | '#items' => [ 127 | Markup::create("Temperament: {$cat_breed->getTemperament()}"), 128 | Markup::create("Description: {$cat_breed->getDescription()}"), 129 | ], 130 | ], 131 | 'image' => [ 132 | '#markup' => $cat_breed->getPicture(), 133 | ], 134 | ]; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /modules/cat_api/src/Form/SearchCatsForm.php: -------------------------------------------------------------------------------- 1 | catApiClient = $cat_api_client; 43 | $this->catBreedFactory = $cat_breed_factory; 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public static function create(ContainerInterface $container) { 50 | return new static( 51 | $container->get('cat_api.client'), 52 | $container->get('cat_api.breed_factory') 53 | ); 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function getFormId() { 60 | return 'cat_api.search_cats'; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function buildForm(array $form, FormStateInterface $form_state, string $breed_id = NULL, int $limit = 3) { 67 | $form['search'] = [ 68 | '#type' => 'fieldset', 69 | '#title' => $this->t('Search'), 70 | 'breed_id' => [ 71 | '#type' => 'select', 72 | '#title' => $this->t('Breed'), 73 | '#options' => array_merge([ 74 | '' => $this->t('Select Breed') 75 | ], $this->getBreedOptions()), 76 | '#default_value' => $breed_id ? $breed_id : '', 77 | '#required' => TRUE, 78 | ], 79 | 'limit' => [ 80 | '#type' => 'select', 81 | '#title' => $this->t('Limit'), 82 | '#options' => array_combine(range(1, 5), range(1, 5)), 83 | '#default_value' => $limit, 84 | ], 85 | 'actions' => [ 86 | '#type' => 'actions', 87 | 'submit' => [ 88 | '#type' => 'submit', 89 | '#value' => $this->t('Search'), 90 | ] 91 | ], 92 | ]; 93 | 94 | if ($breed_id) { 95 | $form['results'] = [ 96 | '#type' => 'fieldset', 97 | '#title' => $this->t('Results'), 98 | 'images' => $this->getImages($breed_id, $limit), 99 | ]; 100 | } 101 | 102 | return $form; 103 | } 104 | 105 | /** 106 | * {@inheritdoc} 107 | */ 108 | public function submitForm(array &$form, FormStateInterface $form_state) { 109 | $form_state->setRedirect('cat_api.search_cats_page', [ 110 | 'breed_id' => $form_state->getValue('breed_id'), 111 | 'limit' => $form_state->getValue('limit'), 112 | ]); 113 | } 114 | 115 | /** 116 | * Get breeds as an options array. 117 | * 118 | * @return array 119 | */ 120 | private function getBreedOptions() { 121 | $breeds = $this->catApiClient->get('breeds'); 122 | $options = []; 123 | foreach ($breeds as $breed) { 124 | $cat_breed = $this->catBreedFactory->createFromBreedResult($breed); 125 | $options[$cat_breed->getId()] = $cat_breed->getName(); 126 | } 127 | return $options; 128 | } 129 | 130 | /** 131 | * Get images for the given breed. 132 | * 133 | * @param string $breed_id 134 | * @param int $limit 135 | * 136 | * @return array 137 | */ 138 | private function getImages($breed_id, $limit) { 139 | $results = $this->catApiClient->get('images/search', [ 140 | 'breed_ids' => $breed_id, 141 | 'limit' => $limit, 142 | ]); 143 | 144 | $items = []; 145 | foreach ($results as $result) { 146 | $cat_breed = $this->catBreedFactory->createFromSearchResult($result); 147 | $items[] = Markup::create("

{$cat_breed->getName()}

{$cat_breed->getPicture()}"); 148 | } 149 | 150 | return [ 151 | '#theme' => 'item_list', 152 | '#items' => $items, 153 | ]; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /modules/cookie_services/src/CookieServiceComplexData.php: -------------------------------------------------------------------------------- 1 | request = $request_stack->getCurrentRequest(); 70 | $this->json = $json; 71 | } 72 | 73 | /** 74 | * {@inheritDoc} 75 | */ 76 | public function getCookieName() { 77 | return $this->cookieName; 78 | } 79 | 80 | /** 81 | * {@inheritDoc} 82 | */ 83 | public function setCookieName(string $cookie_name) { 84 | $this->cookieName = $cookie_name; 85 | } 86 | 87 | /** 88 | * {@inheritDoc} 89 | */ 90 | public function getCookieValue() { 91 | // If we're mid-request and setting a new cookie value, return it. This 92 | // handles the visitor's first request where a cookie could not be set yet. 93 | if (!empty($this->newCookieValue)) { 94 | return $this->newCookieValue; 95 | } 96 | 97 | // Get the value from the cookie and attempt to decode it. 98 | $value = $this->request->cookies->get($this->getCookieName()); 99 | 100 | try { 101 | $value = $this->json->decode($value); 102 | } 103 | catch (\Exception $exception) {} 104 | 105 | return $value; 106 | } 107 | 108 | /** 109 | * {@inheritDoc} 110 | */ 111 | public function setCookieValue($value) { 112 | $this->shouldUpdateCookie = TRUE; 113 | $this->newCookieValue = $value; 114 | } 115 | 116 | /** 117 | * {@inheritDoc} 118 | */ 119 | public function getShouldUpdateCookie() { 120 | return $this->shouldUpdateCookie; 121 | } 122 | 123 | /** 124 | * {@inheritDoc} 125 | */ 126 | public function getShouldDeleteCookie() { 127 | return $this->shouldDeleteCookie; 128 | } 129 | 130 | /** 131 | * {@inheritDoc} 132 | */ 133 | public function setShouldDeleteCookie(bool $delete_cookie = TRUE) { 134 | $this->shouldDeleteCookie = $delete_cookie; 135 | } 136 | 137 | /** 138 | * {@inheritDoc} 139 | */ 140 | public static function getSubscribedEvents() { 141 | return [ 142 | KernelEvents::RESPONSE => 'onResponse', 143 | ]; 144 | } 145 | 146 | /** 147 | * React to the symfony kernel response event by managing visitor cookies. 148 | * 149 | * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event 150 | * The event to process. 151 | */ 152 | public function onResponse(ResponseEvent $event) { 153 | $response = $event->getResponse(); 154 | 155 | // If the cookie should be updated, add new cookie in the response headers. 156 | if ($this->getShouldUpdateCookie()) { 157 | $value = $this->getCookieValue(); 158 | if (is_array($value) || is_object($value)) { 159 | $value = $this->json->encode((array) $value); 160 | } 161 | 162 | $response->headers->setCookie(new Cookie( 163 | $this->getCookieName(), 164 | $value 165 | )); 166 | } 167 | 168 | // If the cookie should be deleted, clear it in the response headers. 169 | if ($this->getShouldDeleteCookie()) { 170 | $response->headers->clearCookie($this->getCookieName()); 171 | } 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /modules/cookie_services/src/CookieServiceSimple.php: -------------------------------------------------------------------------------- 1 | setCookieName('request_count'); 65 | $this->request = $request_stack->getCurrentRequest(); 66 | } 67 | 68 | /** 69 | * {@inheritDoc} 70 | */ 71 | public function getCookieName() { 72 | return $this->cookieName; 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | */ 78 | public function setCookieName(string $cookie_name) { 79 | $this->cookieName = $cookie_name; 80 | } 81 | 82 | /** 83 | * Get the cookie's value. 84 | * 85 | * @return mixed 86 | * Cookie value. 87 | */ 88 | public function getCookieValue() { 89 | // If we're mid-request and setting a new cookie value, return it. This 90 | // handles the visitor's first request where a cookie could not be set yet. 91 | if (!empty($this->newCookieValue)) { 92 | return $this->newCookieValue; 93 | } 94 | 95 | return $this->request->cookies->get($this->getCookieName()); 96 | } 97 | 98 | /** 99 | * Set the cookie's new value. 100 | * 101 | * @param int|string $value 102 | */ 103 | public function setCookieValue($value) { 104 | $this->shouldUpdateCookie = TRUE; 105 | $this->newCookieValue = $value; 106 | } 107 | 108 | /** 109 | * Get the "should update cookie" flag. 110 | * 111 | * @return bool 112 | * Whether or not the cookie should be updated during the response event. 113 | */ 114 | public function getShouldUpdateCookie() { 115 | return $this->shouldUpdateCookie; 116 | } 117 | 118 | /** 119 | * Get the "should delete cookie" flag. 120 | * 121 | * @return bool 122 | * Whether or not the cookie should be deleted during the response event. 123 | */ 124 | public function getShouldDeleteCookie() { 125 | return $this->shouldDeleteCookie; 126 | } 127 | 128 | /** 129 | * Set the "should delete cookie" flag. 130 | * 131 | * @param bool $delete_cookie 132 | * Whether the cookie should be deleted during the response event. 133 | */ 134 | public function setShouldDeleteCookie(bool $delete_cookie = TRUE) { 135 | $this->shouldDeleteCookie = $delete_cookie; 136 | } 137 | 138 | /** 139 | * {@inheritDoc} 140 | */ 141 | public static function getSubscribedEvents() { 142 | return [ 143 | KernelEvents::REQUEST => 'onRequest', 144 | KernelEvents::RESPONSE => 'onResponse', 145 | ]; 146 | } 147 | 148 | /** 149 | * React to the symfony kernel request event. 150 | * 151 | * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event 152 | * The event to process. 153 | */ 154 | public function onRequest(RequestEvent $event) { 155 | if (!$event->isMasterRequest()) { 156 | // The request event can fire more than once. Don't do anything if it's 157 | // not the master request. 158 | return; 159 | } 160 | 161 | $value = $this->getCookieValue(); 162 | $value = (int) $value + 1; 163 | $this->setCookieValue($value); 164 | } 165 | 166 | /** 167 | * React to the symfony kernel response event by managing visitor cookies. 168 | * 169 | * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event 170 | * The event to process. 171 | */ 172 | public function onResponse(ResponseEvent $event) { 173 | $response = $event->getResponse(); 174 | 175 | // If the cookie should be updated, add new cookie in the response headers. 176 | if ($this->getShouldUpdateCookie()) { 177 | $response->headers->setCookie(new Cookie( 178 | $this->getCookieName(), 179 | $this->getCookieValue() 180 | )); 181 | } 182 | 183 | // If the cookie should be deleted, clear it in the response headers. 184 | if ($this->getShouldDeleteCookie()) { 185 | $response->headers->clearCookie($this->getCookieName()); 186 | } 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /modules/custom_entities/practical/src/Entity/PracticalEntity.php: -------------------------------------------------------------------------------- 1 | get('name')->value; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function setName($name) { 71 | $this->set('name', $name); 72 | return $this; 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function getCreatedTime() { 79 | return $this->get('created')->value; 80 | } 81 | 82 | /** 83 | * {@inheritdoc} 84 | */ 85 | public function setCreatedTime($timestamp) { 86 | $this->set('created', $timestamp); 87 | return $this; 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | public function getOwner() { 94 | return $this->get('uid')->entity; 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | */ 100 | public function getOwnerId() { 101 | return $this->get('uid')->target_id; 102 | } 103 | 104 | /** 105 | * {@inheritdoc} 106 | */ 107 | public function setOwner(UserInterface $account) { 108 | $this->set('uid', $account->id()); 109 | return $this; 110 | } 111 | /** 112 | * {@inheritdoc} 113 | */ 114 | public function setOwnerId($uid) { 115 | $this->set('uid', $uid); 116 | return $this; 117 | } 118 | 119 | /** 120 | * {@inheritdoc} 121 | */ 122 | public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { 123 | $fields = parent::baseFieldDefinitions($entity_type); 124 | 125 | $fields['uid'] = BaseFieldDefinition::create('entity_reference') 126 | ->setLabel(t('Authored by')) 127 | ->setDescription(t('The user ID of author of the Practical entity.')) 128 | ->setSetting('target_type', 'user') 129 | ->setSetting('handler', 'default') 130 | ->setDisplayOptions('view', [ 131 | 'label' => 'hidden', 132 | 'type' => 'author', 133 | 'weight' => 0, 134 | ]) 135 | ->setDisplayOptions('form', [ 136 | 'type' => 'entity_reference_autocomplete', 137 | 'weight' => 5, 138 | 'settings' => [ 139 | 'match_operator' => 'CONTAINS', 140 | 'size' => '60', 141 | 'autocomplete_type' => 'tags', 142 | 'placeholder' => '', 143 | ], 144 | ]) 145 | ->setDisplayConfigurable('form', TRUE) 146 | ->setDisplayConfigurable('view', TRUE); 147 | 148 | $fields['name'] = BaseFieldDefinition::create('string') 149 | ->setLabel(t('Name')) 150 | ->setDescription(t('The name of the Practical entity.')) 151 | ->setSettings([ 152 | 'max_length' => 50, 153 | 'text_processing' => 0, 154 | ]) 155 | ->setDefaultValue('') 156 | ->setDisplayOptions('view', [ 157 | 'label' => 'hidden', 158 | 'type' => 'string', 159 | 'weight' => -4, 160 | ]) 161 | ->setDisplayOptions('form', [ 162 | 'type' => 'string_textfield', 163 | 'weight' => -4, 164 | ]) 165 | ->setDisplayConfigurable('form', TRUE) 166 | ->setDisplayConfigurable('view', TRUE); 167 | 168 | $fields['created'] = BaseFieldDefinition::create('created') 169 | ->setLabel(t('Created')) 170 | ->setDescription(t('The time that the entity was created.')); 171 | 172 | $fields['changed'] = BaseFieldDefinition::create('changed') 173 | ->setLabel(t('Changed')) 174 | ->setDescription(t('The time that the entity was last edited.')); 175 | 176 | return $fields; 177 | } 178 | 179 | /** 180 | * {@inheritdoc} 181 | */ 182 | public static function preCreate(EntityStorageInterface $storage_controller, array &$values) { 183 | parent::preCreate($storage_controller, $values); 184 | $values += [ 185 | 'uid' => \Drupal::currentUser()->id(), 186 | ]; 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /modules/cookie_services/src/Form/CookieServiceSandbox.php: -------------------------------------------------------------------------------- 1 | cookieServiceSimple = $cookie_service_simple; 53 | $this->cookieServiceComplex = $cookie_service_complex; 54 | $this->anotherCookieServiceComplex = $another_cookie_service_complex; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | public static function create(ContainerInterface $container) { 61 | return new static( 62 | $container->get('cookie_service_simple'), 63 | $container->get('cookie_service_complex'), 64 | $container->get('another_cookie_service_complex') 65 | ); 66 | } 67 | 68 | /** 69 | * {@inheritDoc} 70 | */ 71 | public function getFormId() { 72 | return 'cookie_service_sandbox_form'; 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | */ 78 | public function buildForm(array $form, FormStateInterface $form_state) { 79 | $form['simple'] = [ 80 | '#type' => 'fieldset', 81 | '#title' => $this->t('Simple Cookie - Requests Count'), 82 | 'current_value' => [ 83 | '#markup' => 'Current Value: ' . $this->cookieServiceSimple->getCookieValue(), 84 | ], 85 | 'new_value' => [ 86 | '#type' => 'number', 87 | '#title' => $this->t('Set New Cookie Value'), 88 | '#default_value' => $this->cookieServiceSimple->getCookieValue(), 89 | ], 90 | 'actions' => [ 91 | '#type' => 'actions', 92 | 'set_simple_cookie_value' => [ 93 | '#type' => 'submit', 94 | '#value' => $this->t('Set New Cookie Value'), 95 | ], 96 | 'delete_simple_cookie' => [ 97 | '#type' => 'submit', 98 | '#value' => $this->t('Delete Cookie'), 99 | ], 100 | ], 101 | ]; 102 | 103 | $complex_data = print_r($this->cookieServiceComplex->getCookieValue(), 1); 104 | $form['complex'] = [ 105 | '#type' => 'fieldset', 106 | '#title' => $this->t("Complex Cookie - {$this->cookieServiceComplex->getCookieName()} - Random Data"), 107 | 'current_value' => [ 108 | '#markup' => Markup::create(" 109 | Current Value:
110 |
{$complex_data}
111 | "), 112 | ], 113 | 'actions' => [ 114 | '#type' => 'actions', 115 | 'set_complex_cookie_value' => [ 116 | '#type' => 'submit', 117 | '#value' => $this->t("Set New Complex Cookie Value - {$this->cookieServiceComplex->getCookieName()}"), 118 | ], 119 | 'delete_complex_cookie' => [ 120 | '#type' => 'submit', 121 | '#value' => $this->t("Delete Complex Cookie - {$this->cookieServiceComplex->getCookieName()}"), 122 | ], 123 | ], 124 | ]; 125 | 126 | $another_complex_data = print_r($this->anotherCookieServiceComplex->getCookieValue(), 1); 127 | $form['another_complex'] = [ 128 | '#type' => 'fieldset', 129 | '#title' => $this->t("Complex Cookie - {$this->anotherCookieServiceComplex->getCookieName()} - Random Data"), 130 | 'current_value' => [ 131 | '#markup' => Markup::create(" 132 | Current Value:
133 |
{$another_complex_data}
134 | "), 135 | ], 136 | 'actions' => [ 137 | '#type' => 'actions', 138 | 'set_another_complex_cookie_value' => [ 139 | '#type' => 'submit', 140 | '#value' => $this->t("Set New Complex Cookie Value - {$this->anotherCookieServiceComplex->getCookieName()}"), 141 | ], 142 | 'delete_another_complex_cookie' => [ 143 | '#type' => 'submit', 144 | '#value' => $this->t("Delete Complex Cookie - {$this->anotherCookieServiceComplex->getCookieName()}"), 145 | ], 146 | ], 147 | ]; 148 | 149 | return $form; 150 | } 151 | 152 | /** 153 | * {@inheritDoc} 154 | */ 155 | public function submitForm(array &$form, FormStateInterface $form_state) { 156 | $trigger = $form_state->getTriggeringElement(); 157 | $trigger_button = NULL; 158 | if (isset($trigger['#parents'], $trigger['#parents'][0])) { 159 | $trigger_button = $trigger['#parents'][0]; 160 | } 161 | 162 | switch ($trigger_button) { 163 | case 'set_simple_cookie_value': 164 | $this->messenger()->addStatus('Setting new simple cookie value.'); 165 | $new_value = $form_state->getValue('new_value') ?? 0; 166 | $this->cookieServiceSimple->setCookieValue($new_value); 167 | break; 168 | 169 | case 'delete_simple_cookie': 170 | $this->messenger()->addStatus('Deleting simple cookie.'); 171 | $this->cookieServiceSimple->setShouldDeleteCookie(TRUE); 172 | break; 173 | 174 | case 'set_complex_cookie_value': 175 | $this->messenger()->addStatus("Setting new complex cookie value for {$this->cookieServiceComplex->getCookieName()}."); 176 | $value = [ 177 | 'some_value' => random_int(0, random_int(100, 1000000)), 178 | 'another_value' => random_int(0, random_int(100, 1000000)), 179 | 'again' => random_int(0, random_int(100, 1000000)), 180 | 'more' => random_int(0, random_int(100, 1000000)), 181 | ]; 182 | $this->cookieServiceComplex->setCookieValue($value); 183 | break; 184 | 185 | case 'delete_complex_cookie': 186 | $this->messenger()->addStatus("Deleting complex cookie {$this->cookieServiceComplex->getCookieName()}."); 187 | $this->cookieServiceComplex->setShouldDeleteCookie(TRUE); 188 | break; 189 | 190 | case 'set_another_complex_cookie_value': 191 | $this->messenger()->addStatus("Setting new complex cookie value for {$this->anotherCookieServiceComplex->getCookieName()}."); 192 | $value = [ 193 | 'first' => random_int(0, random_int(100, 1000000)), 194 | 'last' => random_int(0, random_int(100, 1000000)), 195 | 'city' => random_int(0, random_int(100, 1000000)), 196 | 'state' => random_int(0, random_int(100, 1000000)), 197 | ]; 198 | $this->anotherCookieServiceComplex->setCookieValue($value); 199 | break; 200 | 201 | case 'delete_another_complex_cookie': 202 | $this->messenger()->addStatus("Deleting complex cookie {$this->anotherCookieServiceComplex->getCookieName()}."); 203 | $this->anotherCookieServiceComplex->setShouldDeleteCookie(TRUE); 204 | break; 205 | } 206 | } 207 | 208 | } 209 | --------------------------------------------------------------------------------