vendor and env first commit

This commit is contained in:
2025-03-28 08:52:46 +01:00
parent f8388bc81b
commit 8f26283832
10976 changed files with 1349952 additions and 2 deletions
@@ -0,0 +1,116 @@
<?php
namespace Spatie\QueryBuilder\Concerns;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Spatie\QueryBuilder\Exceptions\AllowedFieldsMustBeCalledBeforeAllowedIncludes;
use Spatie\QueryBuilder\Exceptions\InvalidFieldQuery;
use Spatie\QueryBuilder\Exceptions\UnknownIncludedFieldsQuery;
trait AddsFieldsToQuery
{
protected ?Collection $allowedFields = null;
public function allowedFields($fields): static
{
if ($this->allowedIncludes instanceof Collection) {
throw new AllowedFieldsMustBeCalledBeforeAllowedIncludes();
}
$fields = is_array($fields) ? $fields : func_get_args();
$this->allowedFields = collect($fields)
->map(function (string $fieldName) {
return $this->prependField($fieldName);
});
$this->ensureAllFieldsExist();
$this->addRequestedModelFieldsToQuery();
return $this;
}
protected function addRequestedModelFieldsToQuery()
{
$modelTableName = $this->getModel()->getTable();
$fields = $this->request->fields();
$modelFields = $fields->has($modelTableName) ? $fields->get($modelTableName) : $fields->get('_');
if (empty($modelFields)) {
return;
}
$prependedFields = $this->prependFieldsWithTableName($modelFields, $modelTableName);
$this->select($prependedFields);
}
public function getRequestedFieldsForRelatedTable(string $relation): array
{
$tableOrRelation = config('query-builder.convert_relation_names_to_snake_case_plural', true)
? Str::plural(Str::snake($relation))
: $relation;
$fields = $this->request->fields()
->mapWithKeys(fn ($fields, $table) => [$table => $fields])
->get($tableOrRelation);
if (! $fields) {
return [];
}
if (! $this->allowedFields instanceof Collection) {
// We have requested fields but no allowed fields (yet?)
throw new UnknownIncludedFieldsQuery($fields);
}
return $fields;
}
protected function ensureAllFieldsExist()
{
$modelTable = $this->getModel()->getTable();
$requestedFields = $this->request->fields()
->map(function ($fields, $model) use ($modelTable) {
$tableName = $model;
return $this->prependFieldsWithTableName($fields, $model === '_' ? $modelTable : $tableName);
})
->flatten()
->unique();
$unknownFields = $requestedFields->diff($this->allowedFields);
if ($unknownFields->isNotEmpty()) {
throw InvalidFieldQuery::fieldsNotAllowed($unknownFields, $this->allowedFields);
}
}
protected function prependFieldsWithTableName(array $fields, string $tableName): array
{
return array_map(function ($field) use ($tableName) {
return $this->prependField($field, $tableName);
}, $fields);
}
protected function prependField(string $field, ?string $table = null): string
{
if (! $table) {
$table = $this->getModel()->getTable();
}
if (Str::contains($field, '.')) {
// Already prepended
return $field;
}
return "{$table}.{$field}";
}
}
@@ -0,0 +1,103 @@
<?php
namespace Spatie\QueryBuilder\Concerns;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Spatie\QueryBuilder\AllowedInclude;
use Spatie\QueryBuilder\Exceptions\InvalidIncludeQuery;
use Spatie\QueryBuilder\Includes\IncludeInterface;
trait AddsIncludesToQuery
{
protected ?Collection $allowedIncludes = null;
public function allowedIncludes($includes): static
{
$includes = is_array($includes) ? $includes : func_get_args();
$this->allowedIncludes = collect($includes)
->reject(function ($include) {
return empty($include);
})
->flatMap(function ($include): Collection {
if ($include instanceof Collection) {
return $include;
}
if ($include instanceof IncludeInterface) {
return collect([$include]);
}
if (Str::endsWith($include, config('query-builder.count_suffix', 'Count'))) {
return AllowedInclude::count($include);
}
if (Str::endsWith($include, config('query-builder.exists_suffix', 'Exists'))) {
return AllowedInclude::exists($include);
}
return AllowedInclude::relationship($include);
})
->unique(function (AllowedInclude $allowedInclude) {
return $allowedInclude->getName();
});
$this->ensureAllIncludesExist();
$includes = $this->filterNonExistingIncludes($this->request->includes());
$this->addIncludesToQuery($includes);
return $this;
}
protected function addIncludesToQuery(Collection $includes)
{
$includes->each(function ($include) {
$include = $this->findInclude($include);
$include->include($this);
});
}
protected function findInclude(string $include): ?AllowedInclude
{
return $this->allowedIncludes
->first(function (AllowedInclude $included) use ($include) {
return $included->isForInclude($include);
});
}
protected function ensureAllIncludesExist()
{
if (config('query-builder.disable_invalid_includes_query_exception', false)) {
return;
}
$includes = $this->request->includes();
$allowedIncludeNames = $this->allowedIncludes->map(function (AllowedInclude $allowedInclude) {
return $allowedInclude->getName();
});
$diff = $includes->diff($allowedIncludeNames);
if ($diff->count()) {
throw InvalidIncludeQuery::includesNotAllowed($diff, $allowedIncludeNames);
}
// TODO: Check for non-existing relationships?
}
protected function filterNonExistingIncludes(Collection $includes): Collection
{
if (config('query-builder.disable_invalid_includes_query_exception', false) == false) {
return $includes;
}
return $includes->filter(function ($include) {
return $this->findInclude($include);
});
}
}
@@ -0,0 +1,81 @@
<?php
namespace Spatie\QueryBuilder\Concerns;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\Exceptions\InvalidFilterQuery;
trait FiltersQuery
{
/** @var \Illuminate\Support\Collection */
protected $allowedFilters;
public function allowedFilters($filters): static
{
$filters = is_array($filters) ? $filters : func_get_args();
$this->allowedFilters = collect($filters)->map(function ($filter) {
if ($filter instanceof AllowedFilter) {
return $filter;
}
return AllowedFilter::partial($filter);
});
$this->ensureAllFiltersExist();
$this->addFiltersToQuery();
return $this;
}
protected function addFiltersToQuery()
{
$this->allowedFilters->each(function (AllowedFilter $filter) {
if ($this->isFilterRequested($filter)) {
$value = $this->request->filters()->get($filter->getName());
$filter->filter($this, $value);
return;
}
if ($filter->hasDefault()) {
$filter->filter($this, $filter->getDefault());
return;
}
});
}
protected function findFilter(string $property): ?AllowedFilter
{
return $this->allowedFilters
->first(function (AllowedFilter $filter) use ($property) {
return $filter->isForFilter($property);
});
}
protected function isFilterRequested(AllowedFilter $allowedFilter): bool
{
return $this->request->filters()->has($allowedFilter->getName());
}
protected function ensureAllFiltersExist()
{
if (config('query-builder.disable_invalid_filter_query_exception', false)) {
return;
}
$filterNames = $this->request->filters()->keys();
$allowedFilterNames = $this->allowedFilters->map(function (AllowedFilter $allowedFilter) {
return $allowedFilter->getName();
});
$diff = $filterNames->diff($allowedFilterNames);
if ($diff->count()) {
throw InvalidFilterQuery::filtersNotAllowed($diff, $allowedFilterNames);
}
}
}
@@ -0,0 +1,116 @@
<?php
namespace Spatie\QueryBuilder\Concerns;
use Spatie\QueryBuilder\AllowedSort;
use Spatie\QueryBuilder\Exceptions\InvalidSortQuery;
trait SortsQuery
{
/** @var \Illuminate\Support\Collection */
protected $allowedSorts;
public function allowedSorts($sorts): static
{
$sorts = is_array($sorts) ? $sorts : func_get_args();
$this->allowedSorts = collect($sorts)->map(function ($sort) {
if ($sort instanceof AllowedSort) {
return $sort;
}
return AllowedSort::field(ltrim($sort, '-'));
});
$this->ensureAllSortsExist();
$this->addRequestedSortsToQuery(); // allowed is known & request is known, add what we can, if there is no request, -wait
return $this;
}
/**
* @param array|string|\Spatie\QueryBuilder\AllowedSort $sorts
*
* @return \Spatie\QueryBuilder\QueryBuilder
*/
public function defaultSort($sorts): static
{
$sorts = is_array($sorts) ? $sorts : func_get_args();
return $this->defaultSorts($sorts);
}
/**
* @param array|string|\Spatie\QueryBuilder\AllowedSort $sorts
*
* @return \Spatie\QueryBuilder\QueryBuilder
*/
public function defaultSorts($sorts): static
{
if ($this->request->sorts()->isNotEmpty()) {
// We've got requested sorts. No need to parse defaults.
return $this;
}
$sorts = is_array($sorts) ? $sorts : func_get_args();
collect($sorts)
->map(function ($sort) {
if ($sort instanceof AllowedSort) {
return $sort;
}
return AllowedSort::field($sort);
})
->each(function (AllowedSort $sort) {
$sort->sort($this);
});
return $this;
}
protected function addRequestedSortsToQuery()
{
$this->request->sorts()
->each(function (string $property) {
$descending = $property[0] === '-';
$key = ltrim($property, '-');
$sort = $this->findSort($key);
$sort?->sort($this, $descending);
});
}
protected function findSort(string $property): ?AllowedSort
{
return $this->allowedSorts
->first(function (AllowedSort $sort) use ($property) {
return $sort->isSort($property);
});
}
protected function ensureAllSortsExist(): void
{
if (config('query-builder.disable_invalid_sort_query_exception', false)) {
return;
}
$requestedSortNames = $this->request->sorts()->map(function (string $sort) {
return ltrim($sort, '-');
});
$allowedSortNames = $this->allowedSorts->map(function (AllowedSort $sort) {
return $sort->getName();
});
$unknownSorts = $requestedSortNames->diff($allowedSortNames);
if ($unknownSorts->isNotEmpty()) {
throw InvalidSortQuery::sortsNotAllowed($unknownSorts, $allowedSortNames);
}
}
}