Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestions in collections #275

Open
dan-developer opened this issue Aug 20, 2016 · 3 comments
Open

Suggestions in collections #275

dan-developer opened this issue Aug 20, 2016 · 3 comments

Comments

@dan-developer
Copy link

Collections are often used (at least by me) in application, even when business application.

I will propose the following situation:

  • We have a form where we PostForm 'title', 'body', etc ...
  • Have a TagForm form (only one example), where we 'name'.

The documentation I should do something like this:

@section('content')
    {!! form_start($form) !!}
    <div class="collection-container" data-prototype="{{ form_row($form->tags->prototype()) }}">
        {!! form_row($form->tags) !!}
    </div>
    {!! form_end($form) !!}
    <button type="button" class="add-to-collection">Add to collection</button>
    <script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
    <script>
        $(document).ready(function() {
            $('.add-to-collection').on('click', function(e) {
                e.preventDefault();
                var container = $('.collection-container');
                var count = container.children().length;
                var proto = container.data('prototype').replace(/__NAME__/g, count);
                container.append(proto);
            });
        });
    </script>
@endsection

In {!! form_row($form->tags) !!}

I would not like to have the option to do a foreach manually thus making it a bit more "malleable", something like:

     {{-- the variable $tag is instance of TagForm with special attribute '__key' --}}
        @foreach($form->tags as $tag)
            {!! form_row_collection($tag, 'partials._form-tag') !!}
        @endforeach

In the view:

<div class="row">
    <div class="col-md-6 col-lg-3">
        {{ form_row($tag->name) }}
    </div>
    <div class="col-md-6 col-lg-3">
        {{ form_row($tag->another) }}
    </div>
</div>

So back greater flexibility. There may also be the case that instead of using prototype, we can use ajax to get that "piece" of the tag, and enables reuse the same view.

So to summarize the scenario is registered something, and then it can be edited, and this edition will be "bootstrap" the form where there should already be filled, can be added more items in the collection, so when more flexible better.

As I'm doing manually:

trait CollectionTrait
{

    public function associateCollection($model, $relation, $data)
    {
        if (!is_array($data)) {
            return [];
        }
        /** @var HasOneOrMany $relationConfig */
        $relationConfig = $model->$relation();
        $relatedModel = $relationConfig->getQuery()->getModel();
        $relatedModelClassName = get_class($relatedModel);
        $associated = [];

        // update de existing records
        foreach ($model->$relation as $k => $record) {
            /** @var Model $record */
            if (array_key_exists($k, $data)) {
                $record->fill($data[$k]);
                $associated[] = $record;
                unset($data[$k]);
            }
        }
        // create new records
        foreach ($data as $k => $fields) {
            $newRelation = new $relatedModelClassName($fields);
            $model->$relation->add($newRelation);
            $associated[] = $newRelation;
        }
        return $associated;
    }
}
class DemoFormController extends Controller
{

    use CollectionTrait;

    public function getCreate()
    {
        $carro = new Carro();
        $this->associateCollection($carro, 'tags', app('request')->get('tags'));
        if ($carro->tags->count() === 0) {
           // initialize with one tag
           $carro->tags->add(new CarroTag());
        }
        return view('teste.demo-form.create', compact('carro'));
    }

    public function postCreate()
    {
        // As test "associateCollection"
        $carro = new Carro();
        $this->associateCollection($carro, 'tags', app('request')->get('tags'));
        return redirect()
            ->to('teste/demo-form/create')
            ->withInput(app('request')->all());
    }
}

If you are already doing this, tell me, but I saw in the documentation.

Thank you.

@dan-developer
Copy link
Author

The following example of use, pay attention as I did in getTagForm method in the controller, how do I create a new one.

example.zip

@dan-developer
Copy link
Author

I'm trying to facilitate and not to rewrite code using a new method in FormBuilderTrait. Option is_child causes "Argument 1 passed to form_row() must be an instance of Kris\LaravelFormBuilder\Fields\FormField, null given".

<?php

namespace Yoocloud\LaravelFormBuilder;

use Kris\LaravelFormBuilder\Fields\ChildFormType;

trait FormBuilderTrait
{

    /**
     * Create a Form instance
     *
     * @param string $name Full class name of the form class
     * @param array  $options Options to pass to the form
     * @param array  $data additional data to pass to the form
     *
     * @return \Kris\LaravelFormBuilder\Form
     */
    protected function form($name, array $options = [], array $data = [])
    {
        return \App::make('laravel-form-builder')->create($name, $options, $data);
    }

    /**
     * Create a plain Form instance
     *
     * @param array $options Options to pass to the form
     * @param array $data additional data to pass to the form
     *
     * @return \Kris\LaravelFormBuilder\Form
     */
    protected function plain(array $options = [], array $data = [])
    {
        return \App::make('laravel-form-builder')->plain($options, $data);
    }

    /**
     * Create a Child Form instance
     *
     * @param \Kris\LaravelFormBuilder\Form $form
     * @param string $collectionName
     * @param int $key
     *
     * @return \Kris\LaravelFormBuilder\Fields\ChildFormType
     */
    private function childForm($form, $collectionName, $key = null)
    {
        try {
            // test if collection exists
            $form->$collectionName;
        } catch (\InvalidArgumentException $e) {
            throw new \InvalidArgumentException('The collection ' . $collectionName . ' not exists in '.get_class($form));
        }
        if (is_null($key)) {
            $key = 0;
        }
        $options = $form->$collectionName->getOption('options');
        // bug treatment
        if (array_key_exists('is_child', $options)) {
            unset($options['is_child']);
        }
        $type = $form->$collectionName->getOption('type');
        return new ChildFormType("{$collectionName}[{$key}]", $type, $form, $options);
    }
}
namespace App\Http\Controllers\Teste;

use App\Forms\Teste\PostForm;
use App\Http\Controllers\Controller;
use App\Http\Requests;
use App\Models\Teste\Post;
use Illuminate\Http\Request;
use Kris\LaravelFormBuilder\FormBuilder;
use Yoocloud\LaravelFormBuilder\FormBuilderTrait;

class DefaultController extends Controller
{
    use FormBuilderTrait;

    public function getCreate(FormBuilder $formBuilder)
    {
        $post = new Post();
        $form = $formBuilder->create(PostForm::class, [
            'model' => $post,
            'method' => 'POST',
            'url' => url('teste/default/create'),
        ]);
        return view('teste.default.create', compact('form'));
    }

    public function postCreate(Request $request)
    {
        //
    }

    public function getTagForm()
    {
        $key = \Input::get('key');
        /** @var PostForm $form */
        $form = $this->form(PostForm::class);
        $child = $this->childForm($form, 'tags', $key);
        return view('teste.default.partials._form-tag-child', compact('child'));
    }
}

@dan-developer
Copy link
Author

What would be the proper way to create a new child to a specific index?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant