thedevdojo / voyager

Voyager - The Missing Laravel Admin
https://voyager.devdojo.com
MIT License
11.8k stars 2.67k forks source link

How to create anchor link with the new voyager relationship #2008

Open donmbelembe opened 7 years ago

donmbelembe commented 7 years ago

In olders version we created relationship like this { "relationship": { "key": "id", "label": "name" } }

and we could optionally add a new property to the relationships object in the BREAD details in order to display proper links to relationship records. i.e.:

{ "relationship": { "key": "id", "label": "name", "page_slug": "admin/users" } }

So how to do the same thing with the new version of voyager?

nickbryan commented 6 years ago

Hi,

Has anyone figured this one out?

I am programatically creating my bread as follows:

    $dataRow = $this->dataRow($userDataType, 'user_hasone_user_details_relationship');
        if (!$dataRow->exists) {
            $dataRow->fill([
                'type'         => 'relationship',
                'display_name' => 'Details',
                'required'     => 0,
                'browse'       => 1,
                'read'         => 1,
                'edit'         => 1,
                'add'          => 1,
                'delete'       => 0,
                'details'      => '{"model":"App\\\Models\\\UserDetails","table":"user_details","type":"hasOne","column":"user_id","key":"id","label":"id","pivot_table":"users","pivot":"0"}',
                'order'        => 10,
            ])->save();
        }

Is it possible to add the anchor tag in this way? I have tried adding "page_slug": "/users" to my details string but with no results.

I have had a look at the BreadRelationshipParser trait and it would seem that this is possible?

Any help or advice would be greatly appreciated.

@fletch3555 do you have any advice one this one?

fletch3555 commented 6 years ago

@nickbryan, the link would be handled in the view. Did you check the view (likely bread/browse.blade.php)? I'm not certain this is possible in the current version (meaning it likely broke at some point)

nickbryan commented 6 years ago

Hi @fletch3555, thanks for the reply and apologies for the duplicate ticket. I have had a look at the view it seems it should handle it. Seems like the issue might be in https://github.com/the-control-group/voyager/blob/1.0/src/Http/Controllers/Traits/BreadRelationshipParser.php#L104 this method although as I am not 100% familiar with your code base I cant be certain. It would seem that the $relations = $item->getRelations(); call is not bringing back any relationship data. Not sure if I am missing something?

martyn62 commented 6 years ago

@nickbryan did you ever find a way around this/fix!

donmbelembe commented 6 years ago

Nop, Still looking for a solution

blitzen12 commented 5 years ago

This might be late but I got it working. below are steps I did 1.) create custom fields LinkFormField.php

use TCG\Voyager\FormFields\AbstractHandler;
class LinkFormField extends AbstractHandler {

    protected $codename = "link";

    public function createContent($row, $dataType, $dataTypeContent, $options)
    {
        return view('formfields.link', [
            "row" => $row,
            'options'=> $options,
            'dataType' => $dataType,
            "dataTypeContent" => $dataTypeContent
        ]);
    }

}

under resources/views create subfolder vendor/voyager/formfields create link.blade.php

@if(isset($options->model) && isset($options->type))

    @if(class_exists($options->model))

        @php $relationshipField = $row->field; @endphp

        @if($options->type == 'belongsTo')

            @if(isset($view) && ($view == 'browse' || $view == 'read'))

                @php
                    $relationshipData = (isset($data)) ? $data : $dataTypeContent;
                    $model = app($options->model);
                    $query = $model::find($relationshipData->{$options->column});
                @endphp

                @if(isset($query))
                    <a href="/admin/{{ $options->table }}/{{ $query->{$options->key} }}">{{ $query->{$options->label} }}</a>
                @else
                    <p>{{ __('voyager::generic.no_results') }}</p>                    
                @endif
            @endif
        @endif
    @endif
@endif

2.) Override the view (under resources/views create subfolder vendor/voyager/{{slug_name}} create browse.blade.php) and copy the code fromvendor/tcg/voyager/resources/views/bread/browse.blade.php` look for the following code

@elseif($row->type == 'relationship')
    @include('voyager::formfields.relationship', ['view' => 'browse','options' => $row->details])

change it to this

@elseif($row->type == 'relationship')
    @include('voyager::formfields.link', ['view' => 'browse','options' => $row->details])
sandipiweb commented 4 years ago

This might be late but I got it working. below are steps I did 1.) create custom fields LinkFormField.php

use TCG\Voyager\FormFields\AbstractHandler;
class LinkFormField extends AbstractHandler {

    protected $codename = "link";

    public function createContent($row, $dataType, $dataTypeContent, $options)
    {
        return view('formfields.link', [
            "row" => $row,
            'options'=> $options,
            'dataType' => $dataType,
            "dataTypeContent" => $dataTypeContent
        ]);
    }

}

under resources/views create subfolder vendor/voyager/formfields create link.blade.php

@if(isset($options->model) && isset($options->type))

    @if(class_exists($options->model))

        @php $relationshipField = $row->field; @endphp

        @if($options->type == 'belongsTo')

            @if(isset($view) && ($view == 'browse' || $view == 'read'))

                @php
                    $relationshipData = (isset($data)) ? $data : $dataTypeContent;
                    $model = app($options->model);
                    $query = $model::find($relationshipData->{$options->column});
                @endphp

                @if(isset($query))
                    <a href="/admin/{{ $options->table }}/{{ $query->{$options->key} }}">{{ $query->{$options->label} }}</a>
                @else
                    <p>{{ __('voyager::generic.no_results') }}</p>                    
                @endif
            @endif
        @endif
    @endif
@endif

2.) Override the view (under resources/views create subfolder vendor/voyager/{{slug_name}} create browse.blade.php) and copy the code fromvendor/tcg/voyager/resources/views/bread/browse.blade.php` look for the following code

@elseif($row->type == 'relationship')
    @include('voyager::formfields.relationship', ['view' => 'browse','options' => $row->details])

change it to this

@elseif($row->type == 'relationship')
    @include('voyager::formfields.link', ['view' => 'browse','options' => $row->details])

this file create in which place please aknow

bdelespierre commented 3 years ago

@sandipiweb under App\FormFields

see the doc for custom formfields


I managed to make it work. Here's how I do:

  1. create app/FormFields/LinkFormField.php as stated above by @blitzen12
    
    <?php

namespace App\FormFields;

use TCG\Voyager\FormFields\AbstractHandler;

class LinkFormField extends AbstractHandler { protected $codename = "link";

public function createContent($row, $dataType, $dataTypeContent, $options)
{
    return view('formfields.link', [
        "row" => $row,
        'options'=> $options,
        'dataType' => $dataType,
        "dataTypeContent" => $dataTypeContent
    ]);
}

}


2. register this class in `app/Providers/AppServiceProvider.php` (don't forget to update the `use` statements)

```PHP
    public function register()
    {
        Voyager::addFormField(LinkFormField::class);
    }
  1. create a new view resources/views/vendor/voyager/formfields/link.blade.php with the following contents (also from @blitzen12)

  2. create a new empty folder resources/views/vendor/voyager/bread/

  3. copy vendor/tcg/voyager/resources/views/bread/browse.blade.php and vendor/tcg/voyager/resources/views/bread/read.blade.php into resources/views/vendor/voyager/bread/

  4. in files under resources/views/vendor/voyager/bread/, locate ...

    @elseif($row->type == 'relationship')
    @include('voyager::formfields.relationship', ['view' => 'browse','options' => $row->details])

    ... and replace by ...

    @elseif($row->type == 'relationship')
    @include('voyager::formfields.link', ['view' => 'browse','options' => $row->details])

    (use 'view' => "read" in read.blade.php instead of browse of course).


Now EVERY RELATIONSHIP in browse and read will display link instead of label :+1:

bdelespierre commented 3 years ago

The above solution works only for belongsTo relationships so I needed to find a better option.

  1. Create the view resource/views/vendor/voyager/formfields/relationship.blade.php with the below content
  2. done.

No every type of relationship (including belongsToMany) will display links.

@if(isset($options->model) && isset($options->type))

    @if(class_exists($options->model))

        @php $relationshipField = $row->field; @endphp

        @if($options->type == 'belongsTo')

            @if(isset($view) && ($view == 'browse' || $view == 'read'))

                @php
                    $relationshipData = (isset($data)) ? $data : $dataTypeContent;
                    $model = app($options->model);
                    $query = $model::find($relationshipData->{$options->column});
                    $slug = Voyager::model('DataType')->where('model_name', $options->model)->firstOrFail()->slug;
                @endphp

                @if(isset($query))
                    <p><a href="{{ route("voyager.{$slug}.show", $query->{$options->key}) }}">{{ $query->{$options->label} }}</a></p>
                @else
                    <p>{{ __('voyager::generic.no_results') }}</p>
                @endif

            @else

                <select
                    class="form-control select2-ajax" name="{{ $options->column }}"
                    data-get-items-route="{{route('voyager.' . $dataType->slug.'.relation')}}"
                    data-get-items-field="{{$row->field}}"
                    @if(!is_null($dataTypeContent->getKey())) data-id="{{$dataTypeContent->getKey()}}" @endif
                    data-method="{{ !is_null($dataTypeContent->getKey()) ? 'edit' : 'add' }}"
                >
                    @php
                        $model = app($options->model);
                        $query = $model::where($options->key, old($options->column, $dataTypeContent->{$options->column}))->get();
                    @endphp

                    @if(!$row->required)
                        <option value="">{{__('voyager::generic.none')}}</option>
                    @endif

                    @foreach($query as $relationshipData)
                        <option value="{{ $relationshipData->{$options->key} }}" @if(old($options->column, $dataTypeContent->{$options->column}) == $relationshipData->{$options->key}) selected="selected" @endif>{{ $relationshipData->{$options->label} }}</option>
                    @endforeach
                </select>

            @endif

        @elseif($options->type == 'hasOne')

            @php
                $relationshipData = (isset($data)) ? $data : $dataTypeContent;

                $model = app($options->model);
                $query = $model::where($options->column, '=', $relationshipData->{$options->key})->first();
                $slug = Voyager::model('DataType')->where('model_name', $options->model)->firstOrFail()->slug;

            @endphp

            @if(isset($query))
                <p><a href="{{ route("voyager.{$slug}.show", $query->{$options->key}) }}">{{ $query->{$options->label} }}</a></p>
            @else
                <p>{{ __('voyager::generic.no_results') }}</p>
            @endif

        @elseif($options->type == 'hasMany')

            @if(isset($view) && ($view == 'browse' || $view == 'read'))

                @php
                    $relationshipData = (isset($data)) ? $data : $dataTypeContent;
                    $model = app($options->model);
                    $slug = Voyager::model('DataType')->where('model_name', $options->model)->firstOrFail()->slug;

                    $selected_values = $model::where($options->column, '=', $relationshipData->{$options->key})->get()->map(function ($item, $key) use ($options, $slug) {
                        return sprintf('<a href="%s">%s</a>', route("voyager.{$slug}.show", $item->{$options->key}), $item->{$options->label});
                    })->all();

                @endphp

                @if($view == 'browse')
                    @php
                        $string_values = implode(", ", $selected_values);
                    @endphp
                    @if(empty($selected_values))
                        <p>{{ __('voyager::generic.no_results') }}</p>
                    @else
                        <p>{!! $string_values !!}</p>
                    @endif
                @else
                    @if(empty($selected_values))
                        <p>{{ __('voyager::generic.no_results') }}</p>
                    @else
                        <ul>
                            @foreach($selected_values as $selected_value)
                                <li>{!! $selected_value !!}</li>
                            @endforeach
                        </ul>
                    @endif
                @endif

            @else

                @php
                    $model = app($options->model);
                    $query = $model::where($options->column, '=', $dataTypeContent->{$options->key})->get();
                    $slug = Voyager::model('DataType')->where('model_name', $options->model)->firstOrFail()->slug;
                @endphp

                @if(isset($query))
                    <ul>
                        @foreach($query as $query_res)
                            <li><a href="{{ route("voyager.{$slug}.show", $query_res->{$options->key}) }}">{{ $query_res->{$options->label} }}</a></li>
                        @endforeach
                    </ul>

                @else
                    <p>{{ __('voyager::generic.no_results') }}</p>
                @endif

            @endif

        @elseif($options->type == 'belongsToMany')

            @if(isset($view) && ($view == 'browse' || $view == 'read'))

                @php
                    $relationshipData = (isset($data)) ? $data : $dataTypeContent;
                    $slug = Voyager::model('DataType')->where('model_name', $options->model)->firstOrFail()->slug;

                    $selected_values = isset($relationshipData) ? $relationshipData->belongsToMany($options->model, $options->pivot_table, $options->foreign_pivot_key ?? null, $options->related_pivot_key ?? null, $options->parent_key ?? null, $options->key)->get()->map(function ($item, $key) use ($options, $slug) {
                        return sprintf('<a href="%s">%s</a>', route("voyager.{$slug}.show", $item->{$options->key}), $item->{$options->label});
                    })->all() : array();
                @endphp

                @if($view == 'browse')
                    @php
                        $string_values = implode(", ", $selected_values);
                    @endphp
                    @if(empty($selected_values))
                        <p>{{ __('voyager::generic.no_results') }}</p>
                    @else
                        <p>{!! $string_values !!}</p>
                    @endif
                @else
                    @if(empty($selected_values))
                        <p>{{ __('voyager::generic.no_results') }}</p>
                    @else
                        <ul>
                            @foreach($selected_values as $selected_value)
                                <li>{!! $selected_value !!}</li>
                            @endforeach
                        </ul>
                    @endif
                @endif

            @else
                <select
                    class="form-control @if(isset($options->taggable) && $options->taggable === 'on') select2-taggable @else select2-ajax @endif"
                    name="{{ $relationshipField }}[]" multiple
                    data-get-items-route="{{route('voyager.' . $dataType->slug.'.relation')}}"
                    data-get-items-field="{{$row->field}}"
                    @if(!is_null($dataTypeContent->getKey())) data-id="{{$dataTypeContent->getKey()}}" @endif
                    data-method="{{ !is_null($dataTypeContent->getKey()) ? 'edit' : 'add' }}"
                    @if(isset($options->taggable) && $options->taggable === 'on')
                        data-route="{{ route('voyager.'.\Illuminate\Support\Str::slug($options->table).'.store') }}"
                        data-label="{{$options->label}}"
                        data-error-message="{{__('voyager::bread.error_tagging')}}"
                    @endif
                >

                        @php
                            $selected_values = isset($dataTypeContent) ? $dataTypeContent->belongsToMany($options->model, $options->pivot_table, $options->foreign_pivot_key ?? null, $options->related_pivot_key ?? null, $options->parent_key ?? null, $options->key)->get()->map(function ($item, $key) use ($options) {
                                return $item->{$options->key};
                            })->all() : array();
                            $relationshipOptions = app($options->model)->all();
                        $selected_values = old($relationshipField, $selected_values);
                        @endphp

                        @if(!$row->required)
                            <option value="">{{__('voyager::generic.none')}}</option>
                        @endif

                        @foreach($relationshipOptions as $relationshipOption)
                            <option value="{{ $relationshipOption->{$options->key} }}" @if(in_array($relationshipOption->{$options->key}, $selected_values)) selected="selected" @endif>{{ $relationshipOption->{$options->label} }}</option>
                        @endforeach

                </select>

            @endif

        @endif

    @else

        cannot make relationship because {{ $options->model }} does not exist.

    @endif

@endif