GII CRUD for table with foreign key relationships

So I’ve just started out using this for a few side-projects that I want to play around with and investigate into these types of systems that allow me to get to logic code asap, rather than spending so much time working on and perfecting the setup stuff.

My project is similar to a really simple fixture system. I have tableA, which is purely a ID, name pair for people, then I have tableB, which is a table where I input fixtures between people. It has a column for playerA (foreign key for tableA), playerB (foreign key for tableA), datetime and playerAScore, playerBScore.

I’ve gone through the setup for the Model and CRUD creation of tableA first, then tableB. With going through this way, I thought I’d easily be able to go in and add a new fixture by selecting a player who was already in tableA, from a dropdown list, then the same for playerB, a calendar popup, with a time input for datetime, then integer inputs for the two scores. Now, while the Model and CRUD generator is awesome and has saved me tons of work, I can’t help feel a wee bit underwhelmed when I was presented with very basic integer input for every column (with no type checking on the datetime, I was able to put in ‘a’ as an input, which gave me a zero date and an unavailable foreign key error being shown only when you tried to save a row).

For me, with that type of setup instantly available (form input with real foreign key relationships in and viewable to you from dropdown lists, and similar) would be a huge bonus. Same again with the datetime, I’ve seen plenty of Javascript popup calendars that do that sorta thing.

First things first, I’ve only just dipped my toe in the water with Yii, and am known to be crazy impatient so I don’t actually know if what I’ve currently got is all that is available. If it’s not at all, I’d be more than interested to actually start implementing something along those lines. I’m all about my schemas and ease of setup for stuff like what Gii does.

As I said, I’m not sure if this is already in Yii / Gii, but I don’t know about, but if it’s not, it does seem to be a common way of using these types of systems, so I’d love to know if people are using a standard library, or something, writing a good bit of extra code to do this, or what’s their solution.

Thanks for any feedback or suggestions.

Here are a few resources that may help you:

  • CJuiDatePicker is a jQuery that displays a form field with a jQuery UI datepicker. It’s also the first Google result for yii datepicker.

  • Here’s the Yii wiki entry about adding validation to your models. Basically every model file (the Whatever.php file in the /protected/models/ folder) has a function called rules() where you can define what types of input you accept for a given field, and this addresses how that works more in depth (it’s also the second Google result for yii validation).

  • There’s some info at the top of the CActiveForm documentation page that talks about how you can configure your form do to client-side (AJAX) validation in addition to the server-side validation that Yii always does.

  • As far as foreign key stuff, if you define InnoDB foreign key constraints, Gii will define the relations between your models for you. Here’s the Yii documentation on relations, and here’s a nice Wiki article explaining the difference between the BELONGS_TO and HAS_ONE relations, which can be confusing to new users. (The latter of the two is the top Google result for yii foreign key).

I was trying to point out with the Google links that not only does Yii have really good documentation and resources, but also that it’s all pretty well indexed by Google. I’ve had a lot of luck, when I get stuck on something, checking Google. Yii’s been around 3+ years (I think) now, and especially as you’re getting started you’ll often find that many people have had - and solved! - similar problems to your own.

Feel free to post follow-up questions as you have them, and good luck!

Huge thanks for all that, and while it’s a good bit of what I was asking about, it’s not quite what I have in mind. But the thing I’m most interested about is an intelligent form based on a model.

Basically in the fixture case, what I want is to be able to get this outta the tin, go to the ‘CRUD’ area, add fixture, dropdown list of players for playerA, playerB, a dropdown int list (defined in the model) for playerAScore and playerBScore and the calendar popup for the datetime, as I said.

Now, as you’ve come back with, I’d have to add each pieces of functionality myself. I have absolutely no problem with that whatsoever, but with previous projects, any time I’ve done that, after tons of coding the base setup, and too little work on the actual “business logic” I’d prefer to concentrate on, I read about a system that would have done the majority of what I’d have wanted. This be my research time to try define exactly what I want, and then find out exactly what I need to do to get the system I want and what’s already there already.

Main one is the foreign key section. If you were doing this and were adding in a fixture, would you normally add in a player ID, or would you create some dropdown list to select the player? And if you do create the dropdown list (which I’d have to assume you do, it really is quite common), what’s the most efficient way to do that? Get all the players at start up, then populate a javascript array which in turn populates the options for the dropdown list?

With that process, in my view being so common, I’d assume there’s already fairly comprehensive autodrive systems that will do that for me, but I’ve just not seen them. I have googled about Gii forms that merge data from multiple tables, but the results for that were ambiguous at best. I have also gone through quite a bit of the documentation and bar a good bit of the tutorial, the view for forms, and going into doing funkier things there, seems to run a bit thin, ditto again for the links you’ve shown. They’re all backend logic, which I do know how to technically do, but I’m trying to find out how to do the frontend efficiently too.

Well, Giix is a sort of improved set of templates for Gii. It doesn’t do all of what you’re looking for, but it may get you closer. You can also create your own custom Gii templates.

As far as your dropdown example, I’d do something like this (not tested):

[list=1]

[*]Get all players




$players = Player::model()->findAll(); //pass conditions as parameters here as needed



(findAll in the documentation)

[*]Turn array of player objects into an array of key-value pairs, where the key is the id and the value is the name




$playerList = CHtml::listData($players, 'id', 'name');



(listData in the documentation)

[*]Turn that last into a form dropdown




echo $form->dropDownList($model, 'playerId', $playerList);



(dropDownList in the documentation)

[*]Profit!

[/list]

Once you’re comfortable with that workflow, you may find it more economical to compress it into one statement:




echo $form->dropDownList(

	$model, 

	'playerId', 

	CHtml::listData(Player::model()->findAll(), 'id', 'name')

);



I hope that helps, and maybe is more what you were looking for.

Ask more questions if you have them. Maybe others will chime in as well!

Once again, huge thanks, it really is awesome. That last bit, $form->dropDownList… is exactly the kind of thing I was looking for. There’s still a few things I want to get in for input validation / checking rules, something along the lines of ‘playerA’ ‘notEqual’ ‘playerB’, or something like that. For me, I want to have a system where that would definitely be checked at the DB save point, but also during the input form point, by javascript rules. Something along the lines of if a both dropdown lists are based off the same array of data, if you select a player in the playerA dropdown list, you automatically can’t select him in the playerB dropdown.

Do ya know of any system that’s currently in place for something like that? Honest to God, I have googled this sort of stuff plenty of times, but I can’t think of a way to properly describe that sort of system in a way that googling helps, as I said, any time I do, I generally get quite ambiguous links back that rarely help. The only way I get better info is from forums and StackOverflow and the like :)

Also, reading through the blog demo is something I’ve found quite helpful since you sent me around this path,

http://www.yiiframework.com/doc/blog/1.1/en/post.create

[list=1]

[*]First add something like this to the returned array in the rules() method in your model:




array('playerA', 'compare', 'compareAttribute'=>'playerB', 'operator'=>'!='),



This adds a validation rule that basically says playerA can not equal playerB (validation rules reference - this is super handy, I recommend bookmarking it).

[*]Also in your model, there’s a method called performAjaxValidation($model) that is probably commented out. If so, uncomment it!

[*]In the view with your form, add/change the following bolded line:




<?php $form = $this->beginWidget('CActiveForm', array(

    'id'=>'your-form-id',

[b]    'enableAjaxValidation'=>true,[/b]

   [...]

   ));



[/list]

Rather than disabling select options, it pops up an error if the user selects the same value for playerA and playerB. I’d argue this is better for a couple of reasons:

  • It helps the user create a clearer mental model. Rather than having to wonder, "why is this option disabled?", they get clear, immediate feedback should they happen to select the same player in both dropdowns.

  • You can’t select options in a cross-browser-compatible way without some ugly hacks.

Well now, I’m near 99.9999% where I want to be and once again want to thank you, but there’s just 1 thing where I’d like to change default behaviour to custom that I prefer, both around validation. When you do client validation, if you enter into a state where one of the inputs to your form is now invalid, is there a way to then check the whole form? I mean, in my instance, to get it the way that I want, I have to have


array('playerB', 'compare', 'compareAttribute'=>'playerA', 'operator'=>'!='),

and


array('playerA', 'compare', 'compareAttribute'=>'playerB', 'operator'=>'!='),

Reason for this is that if someone selects a player for ‘A’, then selects a different player for ‘B’, then goes back and selects the player in ‘B’ in ‘A’, without both rules, that’d be considered valid. Vice versa to that as well, if you select a player in ‘A’, then the same in ‘B’, an error pops up. If you then go back to ‘A’, and change that, the error persists at ‘B’, until you change that too. On top of that, if you select the same player in both places, then click ‘Submit’, two errors will pop up, but to get rid of both, you’ve to change both playerA and playerB.

That’s why, if I do find myself in an error state after a change, I’d like my form to run through the full client-side validation as if I’ve pressed the ‘Submit’ button. That way I can have my two rules, but none of the other funkiness would be around. I’ve tried following the logic of the javascript that’s going on for the validation, but I’m not getting my head 100% around it. I’d imagine it’s some sort of call to ‘$.fn.yiiactiveform.validate’, but exactly what and with what parameters to pass to it and from where… I really am not sure.

Thanks again :)

I can’t provide you the solution for your problem but with your validation requirements I would try to write my own PlayerValidator (Declaring Validation Rules, CValidator) class and do the one part of the server-side validation their.

In the CActiveForm docu you found more informations about the three validation types (server, AJAX based, client side). I think you need a combination of the AJAX based and the client side validation.

Hope this informations help you a bit to get into the right direction and finally to a solution

Hmmm, I will look into both of those (as every single link linked to me has been ridiculously informative thusfar), but for the moment, it feels like I’m really close and the only thing I’d like to do is set off a full client-side validation if at any stage the form’s in an error state.

As in;

  • If you’ve just changed something and client-side validation sees that it’s invalid, it then goes through the full form validation

  • If your form is currently in an invalid state and you change something, go through the full form validation

To be honest, I wouldn’t even mind if the full validation happened on every change of every tracked item in the form every time. But again, I can’t see where all of this is currently happening. I’ve stepped through the debugger when I click ‘submit’, but although I’m fairly experienced with Javascript, there really does seem to be some funky JQuery magic going on there that I can’t really follow.