Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions app/Data/ApiKeyData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Data;

use App\Models\ApiKey;

class ApiKeyData extends Data
{
/**
* @param array<int, string>|null $allowed_ips
*/
public function __construct(
public string $identifier,
public ?string $description,
public ?array $allowed_ips,
public ?string $last_used_at,
public string $created_at,
) {}

public static function fromModel(ApiKey $model): self
{
return new self(
identifier: $model->identifier,
description: $model->memo,
allowed_ips: $model->allowed_ips,
last_used_at: $model->last_used_at?->toAtomString(),
created_at: $model->created_at->toAtomString(),
);
}

public function getResourceName(): string
{
return static::getResourceNameStatic();
}

public static function getResourceNameStatic(): string
{
return ApiKey::RESOURCE_NAME;
}
}
77 changes: 77 additions & 0 deletions app/Data/CursorPaginatedDataCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace App\Data;

use Spatie\LaravelData\CursorPaginatedDataCollection as SpatieCursorPaginatedDataCollection;

class CursorPaginatedDataCollection extends SpatieCursorPaginatedDataCollection
{
protected bool $isFractal = false;

public function setFractal(bool $value = true): static
{
$this->isFractal = $value;

return $this;
}

/** @var array<string, mixed> */
protected array $_additional = [];

/**
* @param array<string, mixed> $additional
*/
public function additional(array $additional): static
{
$this->_additional = array_merge($this->_additional, $additional);

return $this;
}
Comment on lines +19 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Same additional() bypass as DataCollection.php/PaginatedDataCollection.php.

Metadata passed to ->additional([...]) is only surfaced when isFractal is true. Outside fractal mode (return $array;, Line 68), it is silently dropped. Same underlying issue and suggested fix as noted on app/Data/DataCollection.php.

Also applies to: 25-69

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Data/CursorPaginatedDataCollection.php` around lines 19 - 23, The
additional metadata stored by additional() is dropped when the collection is
serialized outside fractal mode. Update the non-fractal return path in the
collection serialization logic (lines 25–69) to include _additional alongside
the returned data, while preserving the existing isFractal behavior.


public function toArray(): array
{
$array = parent::toArray();
if ($this->isFractal) {
$resourceKey = method_exists($this->dataClass, 'getResourceNameStatic')
? ($this->dataClass)::getResourceNameStatic()
: strtolower(class_basename($this->dataClass));

$formattedData = [];
$dataItems = $array['data'] ?? [];
foreach ($dataItems as $item) {
if (is_array($item)) {
unset($item['_additional']);
}
$formattedData[] = [
'object' => $resourceKey,
'attributes' => $item,
];
}

$paginator = $this->items;
$cursor = [
'current' => $paginator->cursor() ? $paginator->cursor()->encode() : null,
'prev' => $paginator->previousCursor() ? $paginator->previousCursor()->encode() : null,
'next' => $paginator->nextCursor() ? $paginator->nextCursor()->encode() : null,
'count' => $paginator->count(),
];

$meta = $array['meta'] ?? [];
unset($meta['path'], $meta['per_page'], $meta['next_page_url'], $meta['prev_page_url']);

if (isset($this->_additional['meta'])) {
$meta = array_merge($meta, $this->_additional['meta']);
}

$meta['cursor'] = $cursor;

Comment on lines +53 to +61

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file and nearby context
printf '\n== CursorPaginatedDataCollection.php ==\n'
cat -n app/Data/CursorPaginatedDataCollection.php | sed -n '1,160p'

# Look for related cursor/meta handling in the repo
printf '\n== Search for cursor meta handling ==\n'
rg -n "next_cursor|prev_cursor|cursor" app src . -g '!vendor' -g '!node_modules' || true

# Check composer constraints for Laravel version clues
printf '\n== composer.json ==\n'
cat -n composer.json | sed -n '1,220p'

Repository: pelican-dev/panel

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Determine whether the codebase vendors Laravel paginator classes or references their array shape.
printf '\n== Search for CursorPaginator usage ==\n'
rg -n "CursorPaginator|cursor paginator|toArray\(\)" . -g '!vendor' -g '!node_modules' || true

# If a lockfile exists, inspect installed Laravel component versions.
printf '\n== composer.lock Laravel packages ==\n'
if [ -f composer.lock ]; then
  rg -n '"name": "illuminate/(pagination|support|contracts|collections|container)|"version":' composer.lock | sed -n '1,120p'
fi

Repository: pelican-dev/panel

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== composer.json relevant deps ==\n'
python3 - <<'PY'
import json
from pathlib import Path
data = json.loads(Path('composer.json').read_text())
for section in ('require', 'require-dev'):
    deps = data.get(section, {})
    for name in ('laravel/framework', 'illuminate/pagination', 'spatie/laravel-data'):
        if name in deps:
            print(f'{section}: {name} {deps[name]}')
PY

printf '\n== composer.lock relevant package versions ==\n'
python3 - <<'PY'
import json
from pathlib import Path
p = Path('composer.lock')
if not p.exists():
    print('composer.lock missing')
    raise SystemExit(0)
data = json.loads(p.read_text())
wanted = {'laravel/framework', 'illuminate/pagination', 'spatie/laravel-data'}
for pkg in data.get('packages', []) + data.get('packages-dev', []):
    if pkg.get('name') in wanted:
        print(pkg['name'], pkg.get('version'))
PY

printf '\n== repository references to CursorPaginator ==\n'
rg -n "CursorPaginator|next_cursor|prev_cursor" app config database routes tests .github . -g '!vendor' -g '!node_modules' | sed -n '1,120p'

Repository: pelican-dev/panel

Length of output: 751


🌐 Web query:

Laravel 13.19 CursorPaginator toArray next_cursor prev_cursor meta source

💡 Result:

In Laravel 13.19, the CursorPaginator class's toArray() method defines the structure of the array representation, which serves as the basis for its JSON output [1][2][3]. The toArray() implementation is as follows: return [ 'data' => $this->items->toArray, 'path' => $this->path, 'per_page' => $this->perPage, 'next_cursor' => $this->nextCursor?->encode, 'next_page_url' => $this->nextPageUrl, 'prev_cursor' => $this->previousCursor?->encode, 'prev_page_url' => $this->previousPageUrl, ]; Key structural details include: 1. Data: The records for the current page are located under the 'data' key [1][3]. 2. Cursors: The 'next_cursor' and 'prev_cursor' values are generated by calling the encode method on the respective Cursor objects returned by nextCursor() and previousCursor() [1][2]. 3. Meta: Unlike standard paginators, the default toArray() output for CursorPaginator does not include a dedicated 'meta' key; it includes pagination information (like cursors and URLs) at the top level of the array [1][3]. If you are using API Resources or custom response formatting that requires a 'meta' object, you may need to manually structure the response, as CursorPaginator does not automatically calculate a 'total' count due to the nature of cursor-based pagination [4]. To include such data, developers typically create an explicit structure in the controller or resource collection [4]. Top results: [1][3][5]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== app/Data/Data.php ==\n'
cat -n app/Data/Data.php | sed -n '1,220p'

printf '\n== Cursor collection inheritance points ==\n'
rg -n "CursorPaginatedDataCollection|PaginatedDataCollection|toArray\(\): array|additional\(array" app/Data -n

Repository: pelican-dev/panel

Length of output: 4231


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== app/Data/Data.php ==\n'
cat -n app/Data/Data.php | sed -n '1,220p'

printf '\n== app/Data directory outline ==\n'
find app/Data -maxdepth 1 -type f | sort

Repository: pelican-dev/panel

Length of output: 2985


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== app/Data/PaginatedDataCollection.php ==\n'
cat -n app/Data/PaginatedDataCollection.php | sed -n '1,220p'

printf '\n== app/Data/DataCollection.php ==\n'
cat -n app/Data/DataCollection.php | sed -n '1,220p'

Repository: pelican-dev/panel

Length of output: 5761


🌐 Web query:

Spatie Laravel Data CursorPaginatedDataCollection toArray meta next_cursor prev_cursor

💡 Result:

In the spatie/laravel-data package, CursorPaginatedDataCollection is designed to handle cursor-based pagination for data objects [1][2]. When you call toArray on a CursorPaginatedDataCollection, it transforms the underlying CursorPaginator instance into an array representation [3][4]. Regarding the specific metadata keys next_cursor and prev_cursor: 1. Standard Laravel Behavior: The underlying Laravel CursorPaginator class includes next_cursor and prev_cursor in its own toArray method, along with next_page_url and prev_page_url [4]. 2. Spatie Implementation: Spatie's CursorPaginatedDataCollection essentially wraps this cursor-paginated data [3][1]. When converted to an array, the metadata structure typically mirrors the underlying paginator's output, which includes the cursor information necessary for navigation [4]. 3. Key Clarification: While the package provides the cursor values in the metadata [4], note that in some contexts (like TypeScript transformer generation), there has been historical confusion regarding the naming of these properties compared to standard pagination links [5]. However, when using the standard toArray method, the structure returned by the underlying Laravel CursorPaginator—containing next_cursor and prev_cursor—is preserved within the meta key of the final array output [4]. If you find that these keys are missing in your specific output, verify that you are interacting with the collection as a resource, as Spatie's data objects are designed to automatically include this pagination metadata when returned in responses [6].

Citations:


🌐 Web query:

site:github.com spatie/laravel-data CursorPaginatedDataCollection toArray meta next_cursor prev_cursor

💡 Result:

When using CursorPaginatedDataCollection in spatie/laravel-data, calling the toArray method results in a structure that includes the paginated data items and a meta key containing pagination information [1]. This meta object incorporates cursor-based pagination fields, including next_cursor and prev_cursor [2][1]. The underlying behavior is driven by the Laravel CursorPaginator [3][2]. When transformed to an array, the meta section typically includes the following [2][1]: - data: The collection of transformed data objects [2][1]. - meta: - next_cursor: The encoded string representing the cursor for the next page (or null) [2][1]. - prev_cursor: The encoded string representing the cursor for the previous page (or null) [2][1]. - next_page_url: The URL to the next page [2][1]. - prev_page_url: The URL to the previous page [2][1]. - path: The base path for the paginator [2][1]. - per_page: The number of items per page [2][1]. Note that while these fields exist within the Laravel CursorPaginator implementation [2], some users have noted that when using the spatie/laravel-data package, this information is structured under the meta key [1]. The next_cursor and prev_cursor values are strings generated by the underlying Laravel framework's cursor encoding [2][1].

Citations:


Strip next_cursor / prev_cursor from meta The parent paginator output still carries those keys, so they end up duplicated next to the custom cursor object here. app/Data/CursorPaginatedDataCollection.php:54

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Data/CursorPaginatedDataCollection.php` around lines 53 - 61, Update the
metadata cleanup in the cursor pagination flow to also remove next_cursor and
prev_cursor from $meta before merging additional metadata and assigning the
custom cursor. Preserve the existing removal of path, per_page, next_page_url,
and prev_page_url.

return [
'object' => 'list',
'data' => $formattedData,
'meta' => $meta,
];
}

return $array;
}
}
86 changes: 86 additions & 0 deletions app/Data/Data.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace App\Data;

use Illuminate\Contracts\Pagination\CursorPaginator;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Pagination\AbstractCursorPaginator;
use Illuminate\Pagination\AbstractPaginator;
use Spatie\LaravelData\Data as SpatieData;

abstract class Data extends SpatieData
{
public static string $_collectionClass = DataCollection::class;

public static string $_paginatedCollectionClass = PaginatedDataCollection::class;

public static string $_cursorPaginatedCollectionClass = CursorPaginatedDataCollection::class;

protected bool $isFractal = false;

public function setFractal(bool $value = true): static
{
$this->isFractal = $value;

return $this;
}

public static function collection(mixed $items): DataCollection|PaginatedDataCollection|CursorPaginatedDataCollection
{
if ($items instanceof Paginator || $items instanceof AbstractPaginator) {
/** @var PaginatedDataCollection $collection */
$collection = static::collect(
$items,
static::$_paginatedCollectionClass
);

return $collection;
}

if ($items instanceof CursorPaginator || $items instanceof AbstractCursorPaginator) {
/** @var CursorPaginatedDataCollection $collection */
$collection = static::collect(
$items,
static::$_cursorPaginatedCollectionClass
);

return $collection;
}

/** @var DataCollection $collection */
$collection = static::collect(
$items,
static::$_collectionClass
);

return $collection;
}

public function toArray(): array
{
$array = parent::toArray();
if ($this->isFractal) {
$resourceKey = method_exists($this, 'getResourceName')
? $this->getResourceName()
: strtolower(class_basename($this));

$additional = $this->getAdditionalData();
foreach ($additional as $key => $value) {
unset($array[$key]);
}
Comment on lines +43 to +54

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect vendor source (if present) for the real additional-data accessor.
fd -t d 'laravel-data' vendor/spatie 2>/dev/null
rg -n 'function getAdditional|protected array \$additional|function additional\(' vendor/spatie/laravel-data/src 2>/dev/null

Repository: pelican-dev/panel

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the package version pinned in the repo and any local references to the accessor.
rg -n '"spatie/laravel-data"|spatie/laravel-data' composer.json composer.lock 2>/dev/null || true
rg -n 'getAdditionalData|additionalData|setFractal|toArray\(' app src tests 2>/dev/null || true

Repository: pelican-dev/panel

Length of output: 17360


🌐 Web query:

spatie/laravel-data 4.23.0 getAdditionalData additional trait source

💡 Result:

In spatie/laravel-data, the ability to use the additional method—and by extension manage additional data—is provided by the AppendableData trait [1]. The specific trait is: Spatie\LaravelData\Concerns\AppendableData [1] This trait is one of the standard concerns implemented by the base Data class [2]. It enables the additional(['key' => 'value']) method, which allows you to append extra properties to your data objects or collections when they are transformed into a resource [3][1][4]. While your query specifically mentions getAdditionalData, please note that the public API provided by the AppendableData trait is primarily additional [1][4]. If you are looking for how the data is stored or retrieved internally, you should inspect the source code of the AppendableData trait within the package's src/Concerns directory [2][1]. As of version 4.23.0, there are no breaking changes or new features introduced that modify the core functionality of the AppendableData trait [5][6]. The version 4.23.0 release focused on validation rule overrides and other bug fixes [5][6].

Citations:


🌐 Web query:

site:github.com/spatie/laravel-data "function getAdditionalData" OR "getAdditionalData()" "AppendableData"

💡 Result:

In the context of the spatie/laravel-data package, AppendableData refers to a trait and contract that provides functionality for appending extra properties to data objects when they are transformed [1][2]. The package does not define a specific method named getAdditionalData that returns an AppendableData object. Instead, the package provides an additional method on data objects to add extra properties to the transformed output [3]. Usage Example: SongData::from(Song::first)->additional([ 'year' => 1987, ]); The AppendableData trait is implemented by the base Data class, allowing data objects to include supplemental data alongside their primary attributes when serialized [2]. While you can use additional to pass an array of data, the internal mechanisms for handling these properties are managed by the AppendableData trait and the underlying transformation logic [3][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- app/Data/Data.php ---'
cat -n app/Data/Data.php

echo
echo '--- project references to getAdditionalData ---'
rg -n 'function getAdditionalData|getAdditionalData\(' app tests src 2>/dev/null || true

echo
echo '--- related data classes ---'
for f in app/Data/DataCollection.php app/Data/PaginatedDataCollection.php app/Data/CursorPaginatedDataCollection.php; do
  echo "### $f"
  cat -n "$f"
  echo
done

Repository: pelican-dev/panel

Length of output: 11285


🌐 Web query:

site:github.com/spatie/laravel-data "getAdditionalData" "Data.php" "additional("

💡 Result:

The code snippet getAdditionalData likely refers to the additional() method provided by the spatie/laravel-data package in PHP [1][2]. This method is used to append extra properties to a data object when it is transformed into a resource (e.g., when returned as a JSON response in a Laravel controller) [2]. Key aspects of the additional() method include: 1. Functionality: It allows you to pass an array of key-value pairs that will be included alongside the serialized data object in the final response [1][2]. 2. Usage: You call additional() on an instance of a data object, often before returning it [3][2]. It supports passing a closure, which receives the underlying data object, allowing for dynamic property values [2]. 3. Relationship to Wrapping: When a data object is wrapped (e.g., using wrap('data')), the properties added via additional() are generally not included inside the wrapper; they appear at the top level of the response, similar to how additional() works with standard Laravel JsonResource classes [1][4]. 4. Alternative: You can also use the with() method within your Data class to define additional properties that should always be included whenever that data object is transformed [3][2]. Example usage: $data = SongData::from($song)->additional([ 'year' => 1987, 'slug' => fn(SongData $songData) => Str::slug($songData->title), ]);

Citations:


Replace getAdditionalData()

Spatie\LaravelData\Data doesn't expose this accessor, so any path that hits the fractal branch will throw a fatal error. Use the package's supported additional-data API instead.

🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 52-52: Avoid unused local variables such as '$value'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Data/Data.php` around lines 43 - 54, Replace the unsupported
getAdditionalData() call in Data::toArray() with Spatie Laravel Data’s supported
additional-data API, preserving the existing iteration that removes
additional-data keys from the serialized array.


$response = [
'object' => $resourceKey,
'attributes' => $array,
];

if (isset($additional['meta'])) {
$response['meta'] = $additional['meta'];
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return $response;
}

return $array;
}
}
65 changes: 65 additions & 0 deletions app/Data/DataCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace App\Data;

use Spatie\LaravelData\DataCollection as SpatieDataCollection;

class DataCollection extends SpatieDataCollection
{
protected bool $isFractal = false;

public function setFractal(bool $value = true): static
{
$this->isFractal = $value;

return $this;
}

/** @var array<string, mixed> */
protected array $_additional = [];

/**
* @param array<string, mixed> $additional
*/
public function additional(array $additional): static
{
$this->_additional = array_merge($this->_additional, $additional);

return $this;
}

public function toArray(): array
{
$array = parent::toArray();
if ($this->isFractal) {
$resourceKey = method_exists($this->dataClass, 'getResourceNameStatic')
? ($this->dataClass)::getResourceNameStatic()
: strtolower(class_basename($this->dataClass));

$formattedData = [];
foreach ($array as $item) {
// If the item itself has additional data merged, remove it from attributes
if (is_array($item)) {
unset($item['_additional']);
}
$formattedData[] = [
'object' => $resourceKey,
'attributes' => $item,
];
}

$response = [
'object' => 'list',
'data' => $formattedData,
];

if (isset($this->_additional['meta'])) {
$response['meta'] = $this->_additional['meta'];
}

return $response;
}

return $array;
}
}
103 changes: 103 additions & 0 deletions app/Data/PaginatedDataCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace App\Data;

use Illuminate\Pagination\LengthAwarePaginator;
use Spatie\LaravelData\PaginatedDataCollection as SpatiePaginatedDataCollection;

class PaginatedDataCollection extends SpatiePaginatedDataCollection
{
protected bool $isFractal = false;

public function setFractal(bool $value = true): static
{
$this->isFractal = $value;

return $this;
}

/** @var array<string, mixed> */
protected array $_additional = [];

/**
* @param array<string, mixed> $additional
*/
public function additional(array $additional): static
{
$this->_additional = array_merge($this->_additional, $additional);

return $this;
}
Comment on lines +19 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Same additional() bypass as in DataCollection.php.

This override doesn't delegate to any parent additional() implementation, so metadata is only surfaced when isFractal is true (Line 78-80). In the default (non-fractal) branch (return $array;, Line 90), anything passed to ->additional([...]) is lost. See the equivalent comment on app/Data/DataCollection.php for a suggested defensive fix (merge _additional into $array in the non-fractal branch too).

Also applies to: 25-91

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Data/PaginatedDataCollection.php` around lines 19 - 23, Update
PaginatedDataCollection’s non-fractal response path to merge _additional into
the returned $array before the return, ensuring values supplied through
additional() are preserved. Keep the existing isFractal metadata behavior
unchanged and retain additional()’s current accumulation semantics.


public function toArray(): array
{
$array = parent::toArray();
if ($this->isFractal) {
$resourceKey = method_exists($this->dataClass, 'getResourceNameStatic')
? ($this->dataClass)::getResourceNameStatic()
: strtolower(class_basename($this->dataClass));

$formattedData = [];
$dataItems = $array['data'] ?? [];
foreach ($dataItems as $item) {
if (is_array($item)) {
unset($item['_additional']);
}
$formattedData[] = [
'object' => $resourceKey,
'attributes' => $item,
];
}

$paginator = $this->items;
if (!$paginator instanceof LengthAwarePaginator) {
throw new \InvalidArgumentException('Paginator must be an instance of LengthAwarePaginator');
}

$pagination = [
'total' => $paginator->total(),
'count' => $paginator->count(),
'per_page' => $paginator->perPage(),
'current_page' => $paginator->currentPage(),
'total_pages' => $paginator->lastPage(),
'links' => [],
];
if ($paginator->nextPageUrl()) {
$pagination['links']['next'] = $paginator->nextPageUrl();
}
if ($paginator->previousPageUrl()) {
$pagination['links']['previous'] = $paginator->previousPageUrl();
}

$meta = $array['meta'] ?? [];
unset(
$meta['current_page'],
$meta['first_page_url'],
$meta['from'],
$meta['last_page'],
$meta['last_page_url'],
$meta['next_page_url'],
$meta['path'],
$meta['per_page'],
$meta['prev_page_url'],
$meta['to'],
$meta['total']
);

// Merge custom additional meta
if (isset($this->_additional['meta'])) {
$meta = array_merge($meta, $this->_additional['meta']);
}

$meta['pagination'] = $pagination;

return [
'object' => 'list',
'data' => $formattedData,
'meta' => $meta,
];
}

return $array;
}
}
Loading
Loading