Database query for getting the closest destinations in Drupal 8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
// Database service. It's better to use dependency injection in your class.
$database = \Drupal::service('database');
$earth_radius = 3959;
// Coordinates of the place from which you would like to get nearest places.
$lat = '52.486081';
$lon = '13.404616';
// How many nearest places we would like to show?
$limit = 10;
// Radius to search.
$radius = 100;
$query = $database->select('my_table_name_from_db', 'destination');
$query->fields('destination');
$query->orderBy('distance');
$query->range(0, $limit);
// field_latitude and field_longitude - columns names from 'my_table_name_from_db'
// where coordinates stored.
$expr = "(
$earth_radius * acos(
cos(radians(:lat))
* cos(radians(field_latitude))
* cos(radians(field_longitude) - radians(:lon))
+ sin(radians(:lat))
* sin(radians(field_latitude))
)
)";
$query->addExpression($expr, 'distance', array(':lat' => $lat, ':lon' => $lon));
$query->having('distance <= :radius', array(':radius' => $radius));
$destinations = $query->execute()->fetchAll();
Share Comments

Work with database in Drupal 8

Drupal 8 database

For creating database queries in Drupal 8, we should add dependency to ´Drupal\Core\Database\Connection´ class inside our class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
namespace Drupal\our_custom_module_name;
use Drupal\Core\Database\Connection;
/**
* Our class, where we will communicate with database.
*/
class LetsTalkWithDatabase {
/**
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* Construct.
*/
public function __construct(Connection $database) {
$this->database = $database;
}
/**
* Create function.
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('database')
);
}
}

The Query with condition

1
2
3
4
5
6
$nodes = $this->database('node', 'n')
->fields('n', array('nid', 'title'))
->condition('n.type', 'page')
->condition('n.uid', 1)
->execute()
->fetchAll();

The Query with joining

1
2
3
4
5
$query = $this->database('node', 'n');
$query->innerJoin('users', 'u', 'n.uid = u.uid');
$query->fields('n', array('title'));
$query->fields('u', array('name'));
$nodes = $query->execute()->fetchAll();
Share Comments

How to add a custom validation for the custom entity?

Hello! In this article I would like to describe how we can add custom validation

for our custom entity type.

Let’s use a ´our_custom_module_name´ module, as example, to create an entity validation,
which will check if we have the same entity already (with the same title).

Inside the module folder we should create a constraint:

Constraints in Drupal 8 are also plugins which usually hold a small amount of information about how data is actually being validated, what error message should be used in case of failure and any additional options the validator needs.

/our_custom_module_name/src/Plugin/Validation/Constraint/ExistsEntityWithNameConstraint.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
/**
* @file
* Contains \Drupal\our_custom_module_name\Plugin\Validation\Constraint\ExistsEntityWithNameConstraint.
*/
namespace Drupal\our_custom_module_name\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Constraint for checking if a default entity already exists.
*
* @Constraint(
* id = "ExistsEntityWithName",
* label = @Translation("Entity exists", context = "Validation"),
* type = { "entity" }
* )
*/
class ExistsEntityWithNameConstraint extends Constraint {
/**
* Message shown when the entity already exists.
*
* @var string
*/
public $messageExists = 'Entity %entity already exists';
}

Next, we should create a validator class, where all our validation logic should be placed:

The validator class (which is referenced by the constraint) is responsible for checking the data.

/our_custom_module_name/src/Plugin/Validation/Constraint/ExistsEntityWithNameConstraintValidator.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
/**
* @file
* Contains \Drupal\our_custom_module_name\Plugin\Validation\Constraint\ExistsEntityWithNameConstraintValidator.
*/
namespace Drupal\our_custom_module_name\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the ExistsEntityWithNameConstraint.
*/
class ExistsEntityWithNameConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
/**
* Validator 2.5 and upwards compatible execution context.
*
* @var \Symfony\Component\Validator\Context\ExecutionContextInterface
*/
protected $context;
/**
* @var \Drupal\Core\Entity\EntityTypeManager
*/
protected $entityTypeManager;
/**
* Constructs a new ExistsEntityWithNameConstraintValidator.
*
* @param \Drupal\Core\Entity\EntityTypeManager
* The user storage handler.
*/
public function __construct(EntityTypeManager $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('entity_type.manager'));
}
/**
* {@inheritdoc}
*
* Here we validate entities and check if the same entity
* (with the same name) already exists.
*/
public function validate($entity, Constraint $constraint) {
$name = $entity->name->value;
if (isset($name)) {
$entity_storage = $this->entityTypeManager->getStorage($entity->bundle());
// Check if we already have the same entity.
$ex_entity = $entity_storage->loadByProperties(array('name'=> $name));
if (!empty($ex_entity) && is_array($ex_entity)) {
// Takes entity id.
$ex_entity_id = array_shift($ex_entity)->id();
// If we just update the same entity, then skip validation.
if ($entity->id() != $ex_entity_id) {
$this->context->buildViolation($constraint->messageExists, array(
'%entity' => $name
))
->atPath('name')
->addViolation();
}
}
}
}
}

And last step. We should add annotation to our custom entity to use this validation:

1
2
3
4
5
...
constraints = {
"ExistsEntityWithName" = {}
}
...
Share Comments