It might sound simple but there is significant work to be done here…
I am not sure of what context you have in terms of the relationship between the two dropdown list menus you are trying to connect, but I guess this is how I would do it (in retrospect reading back though this it’s no longer “how” - but actually this is what I did).
Assume that we have two database tables with associated Yii models, one for “Continents” in the world and one for “Countries” in the world, so we will relate to them as this:
Dropdown List 1: Continents
Dropdown List 2: Countries
Depending on which continent you choose from the first dropdown list, we then want this to determine and populate the list of options (countries) available in the second dropdown list - right?
This is what the model attributes look like:
Continents: id | name
Countries: id | name | continent_id
There are other columns contained within these models/database tables, but for this example we only care about those five. Each “country” belongs to a “continent” via the “continent_id” which relates directly to the “id” in the continents table/model.
You can see the relationship in these examples:
$continents = [
[‘id’ => ‘1’, ‘name’ => ‘Europe’],
[‘id’ => ‘2’, ‘name’ => ‘Africa’],
[‘id’ => ‘3’, ‘name’ => ‘Antartica’],
[‘id’ => ‘4’, ‘name’ => ‘Asia’],
[‘id’ => ‘5’, ‘name’ => ‘North America’],
[‘id’ => ‘6’, ‘name’ => ‘Australia’],
[‘id’ => ‘7’, ‘name’ => ‘South America’],
];
$countries = [
[‘id’ => ‘1’, ‘name’ => ‘Australia’, ‘continent_id’ => ‘6’],
[‘id’ => ‘2’, ‘name’ => ‘Belgium’, ‘continent_id’ => ‘1’],
[‘id’ => ‘3’, ‘name’ => ‘Canada’, ‘continent_id’ => ‘5’],
[‘id’ => ‘4’, ‘name’ => ‘China’, ‘continent_id’ => ‘4’],
[‘id’ => ‘5’, ‘name’ => ‘France’, ‘continent_id’ => ‘1’],
[‘id’ => ‘6’, ‘name’ => ‘Germany’, ‘continent_id’ => ‘1’],
[‘id’ => ‘7’, ‘name’ => ‘Great Britain’, ‘continent_id’ => ‘1’],
];
So firstly, we want to create the first dropdown list using the “id” and “name” from the continents model. You can either push in an array in the format of the example above, or you can push in a query from the continents model itself (I will post further below how I would do this).
In the code example below which I have written for you - you can see that the variable $continents is pushed into the dropdown list using the Yii ArrayHelper method via “id” and “name”. We then have some preferences for the dropdown list in the form of a helpful “prompt” (when nothing is selected), a style rule, and more importantly a javascript onchange behaviour which means that when the dropdown list is changed it will make an javascript Ajax request call.
This code will be placed in your view file, typically as part of a form:
<?= $form->field($model, 'continent_id')->dropDownList(
ArrayHelper::map($continents, 'id', 'name'),
[
'prompt'=>'Please choose a continent',
'style' => 'width: 100%;',
'onchange' => '
$.ajax({
type: "POST",
url: "'.Yii::$app->urlManager->createUrl(["site/dependent-dropdown-query"]).'",
data: {search_reference: $(this).val()},
dataType: "json",
success: function(response){
$("#countries-name").prop("disabled", false);
$("#countries-name").empty();
var count = response.length;
if(count === 0) {
$("#countries-name").empty();
$("#countries-name").prop("disabled", "disabled");
$("#countries-name").append("<option value=\'"+id+"\'>Sorry, there are no options available for this selection</option>");
} else {
$("#countries-name").append("<option value=\'"+id+"\'>Please choose a country...</option>");
for(var i = 0; i<count; i++){
var id = response[i][\'id\'];
var name = response[i][\'name\'];
$("#countries-name").append("<option value=\'"+id+"\'>"+name+"</option>");
}
}
}
});
'
]
);
?>
So, as per above, the Ajax call will look for a url in your application, which of course needs to match the Yii url structure as “controller” / “action” (unless you have a module - then you would add it’s name at the front).
In my example I am calling the default “site” controller with an “action” called “dependent-dropdown-query”. It doesn’t need to go in the site controller, that’s just where I placed it as I was writing this for testing purposes. Anyhow, this is what that action looks like in my “site controller”:
This code will be placed inside your preferred controller, as a new action:
public function actionDependentDropdownQuery()
{
$search_reference = Yii::$app->request->post('search_reference');
$query = new Query;
$query->select('id, name')->from('countries')->where(['continent_id' => $search_reference]);
$rows = $query->all();
$data = [];
if(!empty($rows)) {
foreach($rows as $row) {
$data[] = ['id' => $row['id'], 'name' => $row['name']];
}
} else {
$data = '';
}
return $this->asJson($data);
}
So, when the first dropdown list is changed, the Ajax call makes a request to the above controller/action, but as it does that it sends over the “id” value from the continent that has been selected in the first dropdown list as a variable called “search_reference”.
You can see this here:
data: {search_reference: $(this).val()},
Because it is a POST request we grab that and assign it to the $search_reference variable in our controller action which we then subsequently use to do our database query.
Once we have done our database query we assign the results to a new array called $data and push that back via the Ajax request in the format of Json (this is because we are talking to javascript here so we can’t send back a php array or some sort of php output, it must be a json object).
The response format we create is simply in the form of “id” and “name” so we can populate our second dropdown list. If there are no records found then we set $data to empty so we can catch that also if we need to in the response.
Now we place our second dropdownlist in our view file:
<?= $form->field($model, 'name')->dropDownList(
ArrayHelper::map([], 'id', 'name'),
[
'disabled' => 'disabled',
'prompt' => 'Please choose a continent first',
'style' => 'width: 100%;',
],
);
?>
The second dropdown doesn’t look like much does it? Basically it is expecting to receive an associative array of data in the form of “id” and “name” - which is exactly what we created in our controller action as per above.
And that’s pretty much it.
A few points to consider for your own purposes and some functionality which I have included:
- In your controller file you may need to add access to this class so you can do your database query:
use yii\db\Query;
- You may also need to bring in your model classes, so for me it was the countries like so:
use app\models\Countries;
- The first dropdown list will be ready to use, but the second dropdown list will be disabled until you choose something from the first one - nice ux to have
- The prompt will adjust depending on what state the second dropdown list is in
- If there is no data found from your choice in the first dropdown then the second dropdown will understand this and disable itself again
- For each form field dropdown you will need to allocate an appropriate $model, ‘name’ - typically based on the purpose of your form
- In the Ajax request you will see references to and id “#countries-name”, this is the id of your second dropdown list so you will need to edit that as per your preference of whatever it is called
- As for the dropdown list prompts and styling, you can adjust all that as per your preference also
I think that’s it, I will post up code for the database query for the first dropdown array - but it’s late for me now so I will do it tomorrow.
Hopefully this helps you and a few other people out because I see this from time-to-time and no one seems to post a proper answer - it always ends up as guess work.
You could use this same principle for many things like a dropdown list between a car “manufacturer” and their “models”, or whatever.