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

relatedModels in Crud.View? #402

Closed
thumbtech opened this issue Mar 18, 2016 · 21 comments
Closed

relatedModels in Crud.View? #402

thumbtech opened this issue Mar 18, 2016 · 21 comments

Comments

@thumbtech
Copy link

AppController includes:

$this->loadComponent('Crud.Crud', [
    'actions' => [
        'Crud.Index',
        'Crud.Add',
        'Crud.Edit',
        'Crud.View',
        'Crud.Delete'
    ]
]);
$this->Crud->addListener('relatedModels', 'Crud.RelatedModels');

I have a belongsToMany relationship that is correctly handled by Crud.Add & Crud.Edit (i.e., the related data is displayed in a multi-select list and the selected items are saved to the DB).

Now I just want to view the related data in the view. The baked view template is looking for the data, but Crud.View is not querying for it. This seems to be the (odd) default behavior according to doc ...

So now I am setting my AppController to:

$this->loadComponent('Crud.Crud', [
    'actions' => [
        'Crud.Index',
        'Crud.Add',
        'Crud.Edit',
        'View' => [
            'className' => 'Crud.View',
            'relatedModels' => true
        ],
        'Crud.Delete'
    ]
]);
$this->Crud->addListener('relatedModels', 'Crud.RelatedModels');

But still no related data.

This seems pretty hard vs. just baking the controller ... Or?

So if src/Model/Table/ContactsTable.php =

class ContactsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Categories');
    }
}

then the baked controller's view action obviously includes the contain:

public function view($id = null)
{
    $contact = $this->Contacts->get($id, [
        'contain' => ['Categories']
    ]);
    ...

Should Crud.View know to do the same?

@lorenzo
Copy link
Member

lorenzo commented Mar 19, 2016

related models is for sending the required data to the forms for generating select lists. Currently it has no effect on contain for the finder queries

@thumbtech
Copy link
Author

Thank you. That is clear regarding relatedmodels in docs, but I felt a need to confirm since elsewhere in the docs it states that actions are by default compatible with baked views. But views are correctly baked with contain logic, which the actions do not support ...

@dingonv
Copy link

dingonv commented Mar 22, 2016

Could someone please update the docs that in a view action, you will not get related entities in your returned entities? I just spent well over an hour banging my head against a wall wondering why the query for the related entity was returning, but was NOT being added to my output. I finally came in here read this and facedesked...

@bravo-kernel
Copy link
Contributor

@dingonv feel free to PR, documentation can be found in the docs folder of this repo.

@davidyell
Copy link
Member

See the Introduction paragraph in the docs

http://crud.readthedocs.org/en/latest/listeners/related-models.html

@thumbtech
Copy link
Author

IMO the relatedModels docs are fine.

Not to diminish the excellent work here, but the find()->contain logic is so fundamental to cake actions, that it is easily assumed that it must be supported by crud actions. Like dingonv, I banged my head against the wall for a while thinking I had some issue with my code.

I submitted a PR updating the doc intro page, specifically noting this limitation (which I assume will be addressed in the future) where the intro claims compatibility with baked views. If you are not containing related objects in your queries, then you are not truly compatible with baked views IMO.

@davidyell
Copy link
Member

Adding a contain is easy enough, but it would be nice to have a global option for the component to turn on getting related data by default, that's for sure.

@thumbtech
Copy link
Author

@davidyell
Copy link
Member

You can use beforeFind or beforePaginate events. The whole of Crud is powered by events, so if you want to change it's behaviour somehow you'll want to listen for an event.

public function index()
{
    $this->Crud->on('beforePaginate', function(\Cake\Event\Event $event) {
        $this->paginate['contain'] = ['Users'];
    });

    return $this->Crud->execute();
}

@thumbtech
Copy link
Author

Thank you! ... And I will close the PR.

Maybe your example above (which I assume would be a common use case) could appear in the docs? This way my "contain" search of the docs would have provided a hint ...

Also I like the idea of a global contain "switch" to mimic "bin/cake bake controller" functionality. I think my problem was that my ORM almost always contains lots of associations and I was so used to baking the controllers that I assumed similar "default" functionality. FWIW note that a baked controller's view action is probably shorter/simpler than the necessary customized view action required to use crud view with the beforeFind event if you want the contained data! But I get it ... crud is a lot more than just that!

Anyway, keep up the good work!

@davidyell
Copy link
Member

I copied and pasted it from the docs, just changed the array that's all.

If you add the RelatedModels listener globally, that will take care of about 90% of the related data needs.

@thumbtech
Copy link
Author

To be clear: We are on the same team here ...

However, just sayin': If you need relatedModels globally, that implies you have relations. If you have relations, relatedModels does not take care of 90% of your needs: You are more than likely going to need customized actions with beforeFind / beforePaginate events in your controller(s). And if you are using default baked views, both the view and edit actions will definitely require customized actions with beforeFind events to be compatible with those views.

FWIW I am a RTFM guy, but when I evaluated crud, I gave it 30 minutes for a "hello world", and find()->contain is "hello world" for any project I am working on. I searched both the docs and repository code for "contain" to attempt to reverse engineer what I assumed was my issue. Saw events in docs, but did not think I would need to go there to just "light up" my baked views. Sounds like the other user in the thread (dingonv) found the same rabbit hole ...

@davidyell
Copy link
Member

I actually had a stab at this myself, #304

Reckon that might need adding to the docs. I'll create a new issue for this update to the docs.

@dingonv
Copy link

dingonv commented Mar 22, 2016

Hey guys, just so its out there. What i was looking for was the associated data for an event, which links to organizations and locations. on a get to /events/:id i wanted the event and the associated sub entities of organization and location attached as properties to the event.

What i ended up doing was flat out copying the relatedModels listener, changing it's namespace to App\Listener, editing it to change the find method to all instead of list.
In my EventsController, i updated my initialize to

    public function initialize() {
        parent::initialize();
        $this->Crud->addListener('relatedModels','App.RelatedModels');
        $this->Auth->allow([]);

    }

and since i only wanted this in my view action, i made my beforeFilter look like this:

    public function beforeFilter(\Cake\Event\Event $event) {
        parent::beforeFilter($event);
        $this->Crud->listener('relatedModels')->relatedModels(['Organizatons','Locations'], 'view') ;
    }

Then finally, to get the details on the Organization and Location, and to filter the fields i wanted to show, this is how my view action came out:

    public function view($id = null) {
        $user = $this->Auth->identify();
        $event = $this->Events->get($id);
        $related['organization'] = $event->organization_id;
        $related['location'] = $event->location_id;

        $this->Crud->on('relatedModel', function($event) use ($related) {
            if($event->subject()->association->name() === 'Organizations') {
                $event->subject()->query->where(['Organizations.id' => $related['organization']]);
            }
            if($event->subject()->association->name() === 'Locations') {
                $event->subject()->query->where(['Locations.id' => $related['location'] ])
                           ->select(['id','name','address1','address2','city','state','zip']);
            }
        }); 
        $this->Crud->on('beforeRender', function($event) {

        });
        return $this->Crud->execute();
    }

I don't know if there is a simpler way to do this, but this is straightforward enough for me at the moment, and gets the entities i expect, and the fields i expect.

@thumbtech
Copy link
Author

Doh!

I think you just need something like:

public function view($id = null) {
    $user = $this->Auth->identify();

    $this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
        $this->find['contain'] = ['Organizations', 'Locations'];
    });
    return $this->Crud->execute();
}

@dingonv
Copy link

dingonv commented Mar 22, 2016

I tried that. it wasn't processing the contain properly. It may be that i was missing something else and just tired, but at 10 at night after putting dents in the wall, when you find a solution, you go with it ;) I may try that again, but at this point, I am moving on because I want to finish this project and get my life back ;)

@thumbtech
Copy link
Author

👍

@thumbtech
Copy link
Author

I think a small blurb at the end of the quickstart pointing out baseline
support for associated objects using relatedModels and events would be very
welcome. I could try another PR if that would be helpful.

On Tue, Mar 22, 2016 at 12:18 PM, David Yell notifications@github.com
wrote:

It does have a steep learning curve, I do agree. Do you think there are
any improvements which can be made to the documentation to make the
learning curve less severe? Taking into account your own 'hello world'
might not match everyone elses.

Perhaps it's just down to expectations. For me related data being loaded
just means populating drop down lists in my add and edit forms. Which is
stated in the documentation of the listener. If your expectation is that
all your finds will have related data automatically joined, then yes, I can
see where the confusion comes from.


You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
#402 (comment)

@davidyell
Copy link
Member

To add contains to a beforeFind you need to add them to the query.

$this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
    $event->subject()->query->contain(['Organizations', 'Locations']);
});

http://crud.readthedocs.org/en/latest/events.html#crud-beforefind

@davidyell
Copy link
Member

Turns out you're right @thumbtech. Crud will do this for index methods though, but not for view.

@thumbtech
Copy link
Author

... nor for edit, in my experience

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

5 participants