• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AJenbo / agcms / 20972446633

13 Jan 2026 09:01PM UTC coverage: 53.72% (+0.2%) from 53.541%
20972446633

Pull #74

github

web-flow
Merge 8dba43e0f into 498ff829e
Pull Request #74: Add PHP versions 8.4 to 8.5 to CI matrix

248 of 341 new or added lines in 40 files covered. (72.73%)

6 existing lines in 5 files now uncovered.

2780 of 5175 relevant lines covered (53.72%)

13.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/application/inc/Http/Controllers/Admin/ExportController.php
1
<?php
2

3
namespace App\Http\Controllers\Admin;
4

5
use App\Enums\ColumnType;
6
use App\Http\Request;
7
use App\Models\InterfaceRichText;
8
use App\Models\Page;
9
use App\Services\ConfigService;
10
use App\Services\DbService;
11
use App\Services\OrmService;
12
use Exception;
13
use GuzzleHttp\Psr7\Uri;
14
use Symfony\Component\HttpFoundation\Response;
15

16
class ExportController extends AbstractAdminController
17
{
18
    /** @var array<int, string> */
19
    private array $header = [
20
        'ID',
21
        'Type',
22
        'SKU',
23
        'Name',
24
        'Published',
25
        'Is featured?',
26
        'Visibility in catalog',
27
        'Short description',
28
        'Description',
29
        'Date sale price starts',
30
        'Date sale price ends',
31
        'Tax status',
32
        'Tax class',
33
        'In stock?',
34
        'Stock',
35
        'Backorders allowed?',
36
        'Sold individually?',
37
        'Weight (lbs)',
38
        'Length (in)',
39
        'Width (in)',
40
        'Height (in)',
41
        'Allow customer reviews?',
42
        'Purchase note',
43
        'Sale price',
44
        'Regular price',
45
        'Categories',
46
        'Tags',
47
        'Shipping class',
48
        'Images',
49
        'Download limit',
50
        'Download expiry days',
51
        'Parent',
52
        'Grouped products',
53
        'Upsells',
54
        'Cross-sells',
55
        'External URL',
56
        'Button text',
57
        'Position',
58
    ];
59

60
    public function index(Request $request): Response
61
    {
62
        app(DbService::class)->addLoadedTable('bind', 'kat', 'krav', 'maerke', 'sider');
×
63
        $response = $this->cachedResponse();
×
64
        if ($response->isNotModified($request)) {
×
65
            return $response;
×
66
        }
67

68
        $pages = app(OrmService::class)->getByQuery(Page::class, 'SELECT * FROM sider');
×
69

70
        $maxAttributes = 0;
×
71
        foreach ($pages as $page) {
×
72
            $columns = [];
×
73
            foreach ($page->getTables() as $table) {
×
74
                if (!$table->getRows()) {
×
75
                    continue;
×
76
                }
77
                foreach ($table->getColumns() as $column) {
×
78
                    if ($column->type === ColumnType::String || $column->type === ColumnType::Int) {
×
79
                        $columns[$column->title] = true;
×
80
                    }
81
                }
82
            }
83
            $maxAttributes = max(count($columns), $maxAttributes);
×
84
        }
85

86
        for ($i = 1; $i <= $maxAttributes; $i++) {
×
87
            $this->header[] = 'Attribute ' . $i . ' name';
×
88
            $this->header[] = 'Attribute ' . $i . ' value(s)';
×
89
            $this->header[] = 'Attribute ' . $i . ' visible';
×
90
            $this->header[] = 'Attribute ' . $i . ' global';
×
91
        }
92
        $data = [$this->header];
×
93

94
        foreach ($pages as $page) {
×
95
            $tables = $page->getTables();
×
96
            $type = $tables ? 'variable' : 'simple';
×
97
            $productData = $this->getPageData($type, $page);
×
98
            $variations = [];
×
99
            $attributeValues = [];
×
100
            $variationId = 0;
×
101
            foreach ($tables as $table) {
×
102
                $rows = $table->getRows();
×
103
                if (!$rows) {
×
104
                    continue;
×
105
                }
106
                $columns = $table->getColumns();
×
107
                foreach ($columns as $column) {
×
108
                    if (($column->type === ColumnType::String || $column->type === ColumnType::Int) && !isset($attributeValues[$column->title])) {
×
109
                        $attributeValues[$column->title] = [];
×
110
                    }
111
                }
112
                foreach ($rows as $row) {
×
113
                    if ($table->hasLinks() && $row['page']) {
×
114
                        continue;
×
115
                    }
116

117
                    $rowData = $productData;
×
118

119
                    $rowData[0] .= '-' . $variationId;
×
120
                    $rowData[1] = 'variation';
×
121
                    $rowData[2] .= '-' . $variationId;
×
122
                    $rowData[31] = 'id:' . $productData[0];
×
123

124
                    $price = null;
×
125
                    $oldPrice = null;
×
126
                    $salePrice = null;
×
127
                    foreach ($columns as $i => $column) {
×
128
                        if ($column->type === ColumnType::Price) {
×
129
                            $price = $row[$i];
×
130

131
                            continue;
×
132
                        }
133
                        if ($column->type === ColumnType::SalesPrice) {
×
134
                            $salePrice = $row[$i];
×
135

136
                            continue;
×
137
                        }
138
                        if ($column->type === ColumnType::PreviousPrice) {
×
139
                            $oldPrice = $row[$i];
×
140

141
                            continue;
×
142
                        }
143
                        $rowData[] = $column->title;
×
NEW
144
                        $rowData[] = valstring($row[$i]);
×
145
                        $rowData[] = '';
×
146
                        $rowData[] = '0';
×
147

148
                        $attributeValues[$column->title][] = $row[$i];
×
149
                    }
150

151
                    if (!$salePrice && $oldPrice && $price) {
×
152
                        $salePrice = $price;
×
153
                    }
154
                    if ($oldPrice) {
×
155
                        $price = $oldPrice;
×
156
                    }
157
                    if ($salePrice && !$price) {
×
158
                        $price = $salePrice;
×
159
                    }
160
                    if ($salePrice && $salePrice !== $price) {
×
NEW
161
                        $rowData[23] = valstring($salePrice);
×
162
                    }
163
                    if ($price) {
×
NEW
164
                        $rowData[24] = valstring($price);
×
165
                    }
166

167
                    $variations[] = $rowData;
×
168
                    $variationId++;
×
169
                }
170
            }
171
            $attributes = [];
×
172
            foreach ($attributeValues as $title => $values) {
×
173
                $attributes[] = $title;
×
174
                $attributes[] = implode(', ', array_unique($values));
×
175
                $attributes[] = '1';
×
176
                $attributes[] = '0';
×
177
            }
178
            $data[] = array_merge($productData, $attributes);
×
179
            $data = array_merge($data, $variations);
×
180
        }
181

182
        return $this->renderCSV($data);
×
183
    }
184

185
    /**
186
     * @return array<int, string>
187
     */
188
    protected function getPageData(string $type, Page $page): array
189
    {
190
        $categories = $page->getCategories();
×
191
        $paths = [];
×
192
        $isVisible = false;
×
193
        foreach ($categories as $category) {
×
194
            $isVisible |= $category->isVisible();
×
195
            $path = [];
×
196
            foreach ($category->getBranch() as $node) {
×
197
                $path[] = $node->getTitle();
×
198
            }
199
            array_shift($path);
×
200
            $paths[] = implode(' > ', $path);
×
201
        }
202

203
        $accessoryIds = [];
×
204
        foreach ($page->getAccessories() as $accessory) {
×
205
            $accessoryIds[] = 'id:' . $accessory->getId();
×
206
        }
207
        foreach ($page->getTables() as $table) {
×
208
            if (!$table->hasLinks()) {
×
209
                continue;
×
210
            }
211
            foreach ($table->getRows() as $row) {
×
212
                if ($row['page'] instanceof Page) {
×
213
                    $accessoryIds[] = 'id:' . $row['page']->getId();
×
214
                }
215
            }
216
        }
217

218
        $description = $page->getHtml();
×
219
        $description = preg_replace('/<img[^>]*?>/ui', '', $description) ?: '';
×
220

221
        $requirement = $page->getRequirement();
×
222
        $purchaseNote = $requirement ? $requirement->getHtml() : '';
×
223

224
        $price = (string)$page->getPrice();
×
225
        if ($page->getOldPrice()) {
×
226
            $price = (string)$page->getOldPrice();
×
227
        }
228

229
        $salesPrice = '';
×
230
        if ($page->getOldPrice()) {
×
231
            $salesPrice = (string)$page->getPrice();
×
232
        }
233

234
        return [
235
            (string)$page->getId(),
×
236
            $type,
237
            $page->getSku() ?: ('#' . $page->getId()),
×
238
            $page->getTitle(),
×
239
            $page->isInactive() ? '0' : '1',
×
240
            '0',
241
            $isVisible ? 'visible' : 'hidden',
×
242
            $page->getExcerpt(),
×
243
            $description,
244
            '',
245
            '',
246
            'taxable',
247
            '',
248
            $page->isInactive() ? '0' : '1',
×
249
            '',
250
            '1',
251
            '1',
252
            '',
253
            '',
254
            '',
255
            '',
256
            '1',
257
            $purchaseNote,
258
            $salesPrice ?: '',
×
259
            $price ?: '',
×
260
            implode(', ', $paths),
×
NEW
261
            mb_trim($page->getKeywords(), " \n\r\t\v\0,"),
×
262
            '',
263
            implode(', ', $this->extractImages($page)),
×
264
            'n/a',
265
            'n/a',
266
            '',
267
            '',
268
            '',
269
            $accessoryIds ? implode(', ', $accessoryIds) : '',
×
270
            '',
271
            '',
272
            '',
273
        ];
274
    }
275

276
    /**
277
     * @return array<int, string>
278
     */
279
    private function extractImages(InterfaceRichText $richText): array
280
    {
281
        $html = $richText->getHtml();
×
282
        $matches = null;
×
283
        $success = preg_match_all(
×
284
            '/<img[^>]+src="([^"]+)"[^>]*>/iu',
285
            $html,
286
            $matches
287
        );
288

289
        if (false === $success) {
×
290
            throw new Exception('preg_replace failed');
×
291
        }
292

NEW
293
        $urls = array_filter($matches[1]);
×
294

295
        $result = [];
×
296
        foreach ($urls as $url) {
×
297
            $result[] = (string)new Uri(ConfigService::getString('base_url') . $url);
×
298
        }
299

300
        return $result;
×
301
    }
302

303
    /**
304
     * @param array<int, array<int, string>> $data
305
     *
306
     * @throws Exception
307
     */
308
    protected function renderCSV(array $data = []): Response
309
    {
310
        $csv = fopen('php://temp', 'r+b');
×
311
        if ($csv === false) {
×
312
            throw new Exception('Failed to create buffer for CSV data.');
×
313
        }
314

315
        foreach ($data as $row) {
×
316
            fputcsv($csv, $row);
×
317
        }
318

319
        rewind($csv);
×
320

321
        $output = stream_get_contents($csv) ?: null;
×
322

323
        $header = [
×
324
            'Content-Type'        => 'text/csv',
325
            'Content-Disposition' => 'attachment; filename="Export-' . date('c') . '.csv' . '"',
×
326
        ];
327

328
        return new Response($output, Response::HTTP_OK, $header);
×
329
    }
330
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc