Files
online.tbbank.gov.tm-larave…/nova/src/Fields/BelongsTo.php
2024-09-01 18:54:23 +05:00

570 lines
16 KiB
PHP

<?php
namespace Laravel\Nova\Fields;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Laravel\Nova\Contracts\FilterableField;
use Laravel\Nova\Contracts\QueryBuilder;
use Laravel\Nova\Contracts\RelatableField;
use Laravel\Nova\Fields\Filters\EloquentFilter;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Http\Requests\ResourceIndexRequest;
use Laravel\Nova\Resource;
use Laravel\Nova\Rules\Relatable;
use Laravel\Nova\TrashedStatus;
use Laravel\Nova\Util;
/**
* @method static static make(mixed $name, string|null $attribute = null, string|null $resource = null)
*/
class BelongsTo extends Field implements FilterableField, RelatableField
{
use AssociatableRelation;
use DeterminesIfCreateRelationCanBeShown;
use EloquentFilterable;
use FormatsRelatableDisplayValues;
use Peekable;
use ResolvesReverseRelation;
use Searchable;
use SupportsDependentFields;
/**
* The field's component.
*
* @var string
*/
public $component = 'belongs-to-field';
/**
* The class name of the related resource.
*
* @var class-string<\Laravel\Nova\Resource>
*/
public $resourceClass;
/**
* The URI key of the related resource.
*
* @var string
*/
public $resourceName;
/**
* The resolved BelongsTo Resource.
*
* @var \Laravel\Nova\Resource|null
*/
public $belongsToResource;
/**
* The name of the Eloquent "belongs to" relationship.
*
* @var string
*/
public $belongsToRelationship;
/**
* The key of the related Eloquent model.
*
* @var string|int|null
*/
public $belongsToId;
/**
* Indicates if the related resource can be viewed.
*
* @var bool|null
*/
public $viewable;
/**
* The callback that should be run when the field is filled.
*
* @var \Closure(\Laravel\Nova\Http\Requests\NovaRequest, mixed):void
*/
public $filledCallback;
/**
* The attribute that is the inverse of this relationship.
*
* @var string
*/
public $inverse;
/**
* The displayable singular label of the relation.
*
* @var string
*/
public $singularLabel;
/**
* Indicates whether the field should display the "With Trashed" option.
*
* @var bool
*/
public $displaysWithTrashed = true;
/**
* Create a new field.
*
* @param string $name
* @param string|null $attribute
* @param class-string<\Laravel\Nova\Resource>|null $resource
* @return void
*/
public function __construct($name, $attribute = null, $resource = null)
{
parent::__construct($name, $attribute);
$resource = $resource ?? ResourceRelationshipGuesser::guessResource($name);
$this->resourceClass = $resource;
$this->resourceName = $resource::uriKey();
$this->belongsToRelationship = $this->attribute = $attribute ?? ResourceRelationshipGuesser::guessRelation($name);
$this->singularLabel = $name;
}
/**
* Get the relationship name.
*
* @return string
*/
public function relationshipName()
{
return $this->belongsToRelationship;
}
/**
* Get the relationship type.
*
* @return string
*/
public function relationshipType()
{
return 'belongsTo';
}
/**
* Determine if the field should be displayed for the given request.
*
* @param \Illuminate\Http\Request&\Laravel\Nova\Http\Requests\NovaRequest $request
* @return bool
*/
public function authorize(Request $request)
{
return $this->isNotRedundant($request) && parent::authorize($request);
}
/**
* Determine if the field is not redundant.
*
* Ex: Is this a "user" belongs to field in a blog post list being shown on the "user" detail page.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return bool
*/
public function isNotRedundant(NovaRequest $request)
{
return ! $request instanceof ResourceIndexRequest || ! $this->isReverseRelation($request);
}
/**
* Resolve the field's value.
*
* @param mixed $resource
* @param string|null $attribute
* @return void
*/
public function resolve($resource, $attribute = null)
{
$value = null;
if ($resource instanceof Resource || $resource instanceof Model) {
if ($resource->relationLoaded($this->attribute)) {
$value = $resource->getRelation($this->attribute);
} else {
$value = $resource->{$this->attribute}()->withoutGlobalScopes()->getResults();
}
}
if ($value) {
$this->belongsToResource = new $this->resourceClass($value);
$this->belongsToId = Util::safeInt($value->getKey());
$this->value = $this->formatDisplayValue($this->belongsToResource);
$this->viewable = ($this->viewable ?? true) && $this->belongsToResource->authorizedToView(app(NovaRequest::class));
}
}
/**
* Resolve dependent field value.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function resolveDependentValue(NovaRequest $request)
{
return $this->belongsToId ?? $this->resolveDefaultValue($request);
}
/**
* Define the callback that should be used to resolve the field's value.
*
* @param callable $displayCallback
* @return $this
*/
public function displayUsing(callable $displayCallback)
{
return $this->display($displayCallback);
}
/**
* Get the validation rules for this field.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function getRules(NovaRequest $request)
{
$query = $this->buildAssociatableQuery(
$request, $request->{$this->attribute.'_trashed'} === 'true'
)->toBase();
return array_merge_recursive(parent::getRules($request), [
$this->attribute => array_filter([
$this->nullable ? 'nullable' : 'required',
new Relatable($request, $query),
]),
]);
}
/**
* Hydrate the given attribute on the model based on the incoming request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model|\Laravel\Nova\Support\Fluent $model
* @return void
*/
public function fill(NovaRequest $request, $model)
{
$foreignKey = $this->getRelationForeignKeyName($model->{$this->attribute}());
parent::fillInto($request, $model, $foreignKey);
if ($model->isDirty($foreignKey)) {
$model->unsetRelation($this->attribute);
}
if (is_callable($this->filledCallback)) {
call_user_func($this->filledCallback, $request, $model);
}
}
/**
* Hydrate the given attribute on the model based on the incoming request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model|\Laravel\Nova\Support\Fluent $model
* @return mixed
*/
public function fillForAction(NovaRequest $request, $model)
{
if ($request->exists($this->attribute)) {
$value = $request[$this->attribute];
$model->{$this->attribute} = $this->resourceClass::newModel()->query()->find($value);
}
}
/**
* Hydrate the given attribute on the model based on the incoming request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param string $requestAttribute
* @param \Illuminate\Database\Eloquent\Model|\Laravel\Nova\Support\Fluent $model
* @param string $attribute
* @return mixed
*/
protected function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute)
{
if ($request->exists($requestAttribute)) {
$value = $request[$requestAttribute];
$relation = Relation::noConstraints(function () use ($model) {
return $model->{$this->attribute}();
});
if ($this->isValidNullValue($value)) {
$relation->dissociate();
} else {
$relation->associate($relation->getQuery()->withoutGlobalScopes()->find($value));
}
}
}
/**
* Build an associatable query for the field.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param bool $withTrashed
* @return \Laravel\Nova\Contracts\QueryBuilder
*/
public function buildAssociatableQuery(NovaRequest $request, $withTrashed = false)
{
$model = forward_static_call(
[$resourceClass = $this->resourceClass, 'newModel']
);
$query = app()->make(QueryBuilder::class, [$resourceClass]);
$request->first === 'true'
? $query->whereKey($model->newQueryWithoutScopes(), $request->current)
: $query->search(
$request, $model->newQuery(), $request->search,
[], [], TrashedStatus::fromBoolean($withTrashed)
);
return $query->tap(function ($query) use ($request, $model) {
if (is_callable($this->relatableQueryCallback)) {
call_user_func($this->relatableQueryCallback, $request, $query);
return;
}
forward_static_call($this->associatableQueryCallable($request, $model), $request, $query, $this);
});
}
/**
* Get the associatable query method name.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model $model
* @return array
*/
protected function associatableQueryCallable(NovaRequest $request, $model)
{
return ($method = $this->associatableQueryMethod($request, $model))
? [$request->resource(), $method]
: [$this->resourceClass, 'relatableQuery'];
}
/**
* Get the associatable query method name.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model $model
* @return string|null
*/
protected function associatableQueryMethod(NovaRequest $request, $model)
{
$method = 'relatable'.Str::plural(class_basename($model));
if (method_exists($request->resource(), $method)) {
return $method;
}
}
/**
* Format the given associatable resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param mixed $resource
* @return array
*/
public function formatAssociatableResource(NovaRequest $request, $resource)
{
return array_filter([
'avatar' => $resource->resolveAvatarUrl($request),
'display' => $this->formatDisplayValue($resource),
'subtitle' => $resource->subtitle(),
'value' => Util::safeInt($resource->getKey()),
]);
}
/**
* Specify if the related resource can be viewed.
*
* @param bool $value
* @return $this
*/
public function viewable($value = true)
{
$this->viewable = $value;
return $this;
}
/**
* Specify a callback that should be run when the field is filled.
*
* @param \Closure(\Laravel\Nova\Http\Requests\NovaRequest, mixed):void $callback
* @return $this
*/
public function filled($callback)
{
$this->filledCallback = $callback;
return $this;
}
/**
* Set the value for the field.
*
* @param mixed $value
* @return void
*/
public function setValue($value)
{
$this->belongsToId = Util::safeInt($value);
$this->value = $value;
}
/**
* Set the attribute name of the inverse of the relationship.
*
* @param string $inverse
* @return $this
*/
public function inverse($inverse)
{
$this->inverse = $inverse;
return $this;
}
/**
* Set the displayable singular label of the resource.
*
* @param string $singularLabel
* @return $this
*/
public function singularLabel($singularLabel)
{
$this->singularLabel = $singularLabel;
return $this;
}
/**
* hides the "With Trashed" option.
*
* @return $this
*/
public function withoutTrashed()
{
$this->displaysWithTrashed = false;
return $this;
}
/**
* Return the sortable uri key for the field.
*
* @return string
*/
public function sortableUriKey()
{
$request = app(NovaRequest::class);
return $this->getRelationForeignKeyName($request->newResource()->resource->{$this->attribute}());
}
/**
* Make the field filter.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return \Laravel\Nova\Fields\Filters\Filter|null
*/
protected function makeFilter(NovaRequest $request)
{
if ($request->viaRelationship()
&& ($request->relationshipType ?? null) === 'hasMany'
&& $this->resourceClass::uriKey() === $request->viaResource
) {
return null;
}
return new EloquentFilter($this);
}
/**
* Define filterable attribute.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return string
*/
protected function filterableAttribute(NovaRequest $request)
{
return $this->getRelationForeignKeyName($request->newResource()->resource->{$this->attribute}());
}
/**
* Define the default filterable callback.
*
* @return callable(\Laravel\Nova\Http\Requests\NovaRequest, \Illuminate\Database\Eloquent\Builder, mixed, string):void
*/
protected function defaultFilterableCallback()
{
return function (NovaRequest $request, $query, $value, $attribute) {
$query->where($attribute, '=', $value);
};
}
/**
* Prepare the field for JSON serialization.
*
* @return array
*/
public function serializeForFilter()
{
return transform($this->jsonSerialize(), function ($field) {
return [
'debounce' => $field['debounce'],
'displaysWithTrashed' => $field['displaysWithTrashed'],
'label' => $this->resourceClass::label(),
'resourceName' => $field['resourceName'],
'searchable' => $field['searchable'],
'withSubtitles' => $field['withSubtitles'],
'uniqueKey' => $field['uniqueKey'],
];
});
}
/**
* Prepare the field for JSON serialization.
*
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
return with(app(NovaRequest::class), function ($request) {
$viewable = ! is_null($this->viewable) ? $this->viewable : $this->resourceClass::authorizedToViewAny($request);
return array_merge([
'belongsToId' => $this->belongsToId,
'relationshipType' => $this->relationshipType(),
'belongsToRelationship' => $this->belongsToRelationship,
'debounce' => $this->debounce,
'displaysWithTrashed' => $this->displaysWithTrashed,
'label' => $this->resourceClass::label(),
'peekable' => $this->isPeekable($request),
'hasFieldsToPeekAt' => $this->hasFieldsToPeekAt($request),
'resourceName' => $this->resourceName,
'reverse' => $this->isReverseRelation($request),
'searchable' => $this->isSearchable($request),
'withSubtitles' => $this->withSubtitles,
'showCreateRelationButton' => $this->createRelationShouldBeShown($request),
'singularLabel' => $this->singularLabel,
'viewable' => $viewable,
], parent::jsonSerialize());
});
}
}