Passed
Push — master ( b318ac...f0530b )
by Paul
07:00 queued 20s
created

ImportReviews::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
ccs 0
cts 5
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Commands;
4
5
use GeminiLabs\League\Csv\Exception;
6
use GeminiLabs\League\Csv\Reader;
7
use GeminiLabs\League\Csv\Statement;
8
use GeminiLabs\SiteReviews\Contracts\CommandContract as Contract;
9
use GeminiLabs\SiteReviews\Database\ReviewManager;
10
use GeminiLabs\SiteReviews\Helpers\Arr;
11
use GeminiLabs\SiteReviews\Helpers\Str;
12
use GeminiLabs\SiteReviews\Modules\Date;
13
use GeminiLabs\SiteReviews\Modules\Notice;
14
use GeminiLabs\SiteReviews\Modules\Rating;
15
use GeminiLabs\SiteReviews\Request;
16
use GeminiLabs\SiteReviews\Upload;
17
18
class ImportReviews extends Upload implements Contract
19
{
20
    const ALLOWED_DATE_FORMATS = [
21
        'd-m-Y', 'd/m/Y',
22
        'm-d-Y', 'm/d/Y',
23
        'Y-m-d', 'Y-m-d H:i:s', 'Y/m/d',
24
    ];
25
26
    const ALLOWED_DELIMITERS = [
27
        ',', ';',
28
    ];
29
30
    const REQUIRED_KEYS = [
31
        'content', 'date', 'rating',
32
    ];
33
34
    /**
35
     * @var string
36
     */
37
    protected $date_format = 'Y-m-d';
38
39
    /**
40
     * @var string
41
     */
42
    protected $delimiter = ',';
43
44
    /**
45
     * @var string[]
46
     */
47
    protected $errors = [];
48
49
    /**
50
     * @var int
51
     */
52
    protected $totalRecords = 0;
53
54
    public function __construct(Request $request)
55
    {
56
        $this->date_format = Str::restrictTo(static::ALLOWED_DATE_FORMATS, $request->date_format, 'Y-m-d');
57
        $this->delimiter = Str::restrictTo(static::ALLOWED_DELIMITERS, $request->delimiter, ',');
58
        $this->errors = [];
59
    }
60
61
    /**
62
     * @return void
63
     */
64
    public function handle()
65
    {
66
        if (!$this->validateUpload()
67
            || !$this->validateExtension('.csv')) {
68
            return;
69
        }
70
        glsr()->store('import', true);
71
        $result = $this->import();
72
        glsr()->discard('import');
73
        if (false !== $result) {
74
            $this->notify($result);
75
        }
76
    }
77
78
    /**
79
     * @return int|bool
80
     */
81
    protected function import()
82
    {
83
        define('WP_IMPORTING', true);
84
        if (!ini_get('auto_detect_line_endings')) {
85
            ini_set('auto_detect_line_endings', '1');
86
        }
87
        require_once glsr()->path('vendors/thephpleague/csv/functions_include.php');
88
        try {
89
            wp_raise_memory_limit('admin');
90
            $reader = Reader::createFromPath($this->file()->tmp_name);
91
            $reader->setDelimiter($this->delimiter);
92
            $reader->setHeaderOffset(0);
93
            $header = array_map('trim', $reader->getHeader());
94
            if (!empty(array_diff(static::REQUIRED_KEYS, $header))) {
95
                throw new Exception('The CSV import header is missing the required columns (did you select the correct delimiter?).');
96
            }
97
            $this->totalRecords = count($reader);
98
            $records = Statement::create()
99
                ->where(function ($record) {
100
                    return $this->validateRecord($record);
101
                })
102
                ->process($reader, $header);
103
            return $this->importRecords($records);
104
        } catch (Exception $e) {
105
            glsr(Notice::class)->addError($e->getMessage());
106
            return false;
107
        }
108
    }
109
110
    /**
111
     * @return int
112
     */
113
    protected function importRecords($records)
114
    {
115
        foreach ($records as $offset => $record) {
116
            $date = \DateTime::createFromFormat($this->date_format, $record['date']);
117
            $record['date'] = $date->format('Y-m-d H:i:s'); // format the provided date
118
            $request = new Request($record);
119
            $command = new CreateReview($request);
120
            glsr(ReviewManager::class)->createRaw($command);
121
        }
122
        return count($records);
123
    }
124
125
    /**
126
     * @return void
127
     */
128
    protected function notify($result)
129
    {
130
        $skippedRecords = max(0, $this->totalRecords - $result);
131
        $notice = sprintf(
132
            _nx('%s review was imported.', '%s reviews were imported.', $result, 'admin-text', 'site-reviews'),
133
            number_format_i18n($result)
134
        );
135
        if (0 === $skippedRecords) {
136
            glsr(Notice::class)->addSuccess($notice);
137
            return;
138
        }
139
        $skipped = sprintf(
140
            _nx('%s entry was skipped.', '%s entries were skipped.', $skippedRecords, 'admin-text', 'site-reviews'),
141
            number_format_i18n($skippedRecords)
142
        );
143
        $consoleLink = sprintf(_x('See the %s for more details.', 'admin-text', 'site-reviews'),
144
            sprintf('<a href="%s">%s</a>)',
145
                admin_url('edit.php?post_type='.glsr()->post_type.'&page=tools#tab-console'),
146
                _x('Console', 'admin-text', 'site-reviews')
147
            )
148
        );
149
        glsr(Notice::class)->addWarning(sprintf('%s (%s)', sprintf('%s %s', $notice, $skipped), $consoleLink));
150
        glsr_log()->warning(sprintf('One or more of the following errors were encountered during import: %s', Str::naturalJoin($this->errors)));
151
    }
152
153
    /**
154
     * @return bool
155
     */
156
    protected function validateRecord(array $record)
157
    {
158
        $required = [
159
            'date' => glsr(Date::class)->isValid(Arr::get($record, 'date'), $this->date_format),
160
            'content' => !empty($record['content']),
161
            'rating' => glsr(Rating::class)->isValid(Arr::get($record, 'rating')),
162
        ];
163
        if (count(array_filter($required)) === 3) {
164
            return true;
165
        }
166
        $errorMessages = [
167
            'date' => _x('invalid date', 'admin-text', 'site-reviews'),
168
            'content' => _x('invalid content', 'admin-text', 'site-reviews'),
169
            'rating' => _x('invalid rating', 'admin-text', 'site-reviews'),
170
        ];
171
        $errors = array_intersect_key($errorMessages, array_diff_key($required, array_filter($required)));
172
        $this->errors = array_merge($this->errors, $errors);
173
        return false;
174
    }
175
}
176