Completed
Push — 2.0 ( 0357a9...3fe951 )
by Peter
08:22 queued 10s
created

DateAdd   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 99
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 9
lcom 1
cbo 3
dl 0
loc 99
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 2
A transform() 0 7 1
B execute() 0 24 6
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of the Happyr Doctrine Specification package.
6
 *
7
 * (c) Tobias Nyholm <[email protected]>
8
 *     Kacper Gunia <[email protected]>
9
 *     Peter Gribanov <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Happyr\DoctrineSpecification\Operand\PlatformFunction;
16
17
use Doctrine\ORM\QueryBuilder;
18
use Happyr\DoctrineSpecification\Exception\InvalidArgumentException;
19
use Happyr\DoctrineSpecification\Operand\ArgumentToOperandConverter;
20
use Happyr\DoctrineSpecification\Operand\Operand;
21
22
/**
23
 * Add the number of days to a given date. (Supported units are YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND).
24
 */
25
final class DateAdd implements Operand
26
{
27
    public const YEAR = 'YEAR';
28
    public const MONTH = 'MONTH';
29
    public const WEEK = 'WEEK';
30
    public const DAY = 'DAY';
31
    public const HOUR = 'HOUR';
32
    public const MINUTE = 'MINUTE';
33
    public const SECOND = 'SECOND';
34
35
    private const UNITS = [
36
        self::YEAR,
37
        self::MONTH,
38
        self::WEEK,
39
        self::DAY,
40
        self::HOUR,
41
        self::MINUTE,
42
        self::SECOND,
43
    ];
44
45
    /**
46
     * @var \DateTimeInterface|string|Operand
47
     */
48
    private $date;
49
50
    /**
51
     * @var int|Operand
52
     */
53
    private $value;
54
55
    /**
56
     * @var string
57
     */
58
    private $unit;
59
60
    /**
61
     * @param \DateTimeInterface|string|Operand $date
62
     * @param int|Operand                       $value
63
     * @param string                            $unit
64
     */
65
    public function __construct($date, $value, string $unit)
66
    {
67
        if (!in_array(strtoupper($unit), self::UNITS, true)) {
68
            throw new InvalidArgumentException(sprintf(
69
                'The DATE_ADD() function support "%s" units, got "%s" instead.',
70
                implode('", "', self::UNITS),
71
                $unit
72
            ));
73
        }
74
75
        $this->date = $date;
76
        $this->value = $value;
77
        $this->unit = $unit;
78
    }
79
80
    /**
81
     * @param QueryBuilder $qb
82
     * @param string       $context
83
     *
84
     * @return string
85
     */
86
    public function transform(QueryBuilder $qb, string $context): string
87
    {
88
        $date = ArgumentToOperandConverter::toField($this->date)->transform($qb, $context);
89
        $value = ArgumentToOperandConverter::toValue($this->value)->transform($qb, $context);
90
91
        return sprintf('DATE_ADD(%s, %s, \'%s\')', $date, $value, $this->unit);
92
    }
93
94
    /**
95
     * @param mixed[]|object $candidate
96
     *
97
     * @return \DateTimeImmutable
98
     */
99
    public function execute($candidate): \DateTimeImmutable
100
    {
101
        $date = ArgumentToOperandConverter::toField($this->date)->execute($candidate);
102
        $value = ArgumentToOperandConverter::toValue($this->value)->execute($candidate);
103
104
        if (!$date instanceof \DateTimeInterface) {
105
            throw new InvalidArgumentException(sprintf(
106
                'The date should be instance of "%s", got "%s" instead.',
107
                \DateTimeInterface::class,
108
                is_object($date) ? get_class($date) : gettype($date)
109
            ));
110
        }
111
112
        if (!is_int($value) && !is_numeric($value) && !ctype_digit($value)) {
113
            throw new InvalidArgumentException(sprintf(
114
                'The value should be an integer, got "%s" instead.',
115
                gettype($value)
116
            ));
117
        }
118
119
        $new_date = new \DateTimeImmutable($date->format('Y-m-d H:i:s'), $date->getTimezone());
120
121
        return $new_date->modify(sprintf('+%d %s', $value, $this->unit));
122
    }
123
}
124