Country Filters

Country Filters #

How to handle countries in your application? Well it depends on the use case. However, if you have a country table in you database, it is not a good solution.

There are already finished libraries which will handle this for you. Symfony has a great library to solve this problem. It’s all done, you just need to use it. Firstly, start with the dependency:

lando composer require symfony/intl

Then you can define your app/Filters/CountryFilter.php:

<?php

declare(strict_types=1);

namespace App\Filters;

use Contributte\Translation\Translator;
use Latte;
use Symfony\Component\Intl\Countries;


final class CountryFilter extends AbstractFilter
{
	private Translator $translator;

	public function __construct(Translator $translator)
	{
		$this->translator = $translator;
	}

	public function install(Latte\Engine $engine): void
	{
		$engine->addFilter('countryName', [$this, 'countryName']);
	}

	public function countryName(?string $country): ?string
	{
		return $country ? Countries::getName($country, $this->translator->getLocale()) : null;
	}

}

With these (and also with automatic registration) you can write country name in current language from translator for example like this:

{$company->country|countryName}

In your application, you should use country codes. It is normalized form of how to handle country information. You don’t need any database table with countries. You will just use 2-letter code in your table.

Column country in your database

It is a best practice to type your column to char(2) with collation ascii_bin. This will ensure that your country codes will be smaller in terms of memory, and warn you when you will try to store there something else.

Country Flags #

To make your frontend more user-friendly, you can show country flags instead of country names. Flag Icons CSS is here to help. You just need to append to your styles.

lando yarn add flag-icons

Append to your SCSS styles:

@import "~flag-icons/sass/flag-icons";

And you can create filter method countryFlag:

final class CountryFilter extends AbstractFilter
{

	public function install(Latte\Engine $engine): void
	{
		// ...
		$engine->addFilter('countryFlag', [$this, 'countryFlag']);
	}

	// ...

	public function countryFlag(?string $country): ?Html
	{
		if ($country === null) {
			return null;
		}

		return Html::el('i', [
			'class' => 'flag-icon flag-icon-' . Strings::lower($country),
			'data-bs-toggle' => 'tooltip',
			'data-bs-title' => $this->countryName($country),
		]);
	}
}

And use it like this:

{$company->country|countryFlag}

Countries in Form Factories #

Until now, everithing looks fine. But how this all fits inside my application?

When you edit your entities in forms, you need to set those country codes somehow. Your users do not need to insert country codes in inputs when editing something. They don’t know them and don’t want to know them. Select box is here to help!

You will need to define method e.g. getCountryItems in your country filter to provide these items in your form factory. For example:

final class CountryFilter extends AbstractFilter
{

	// ...

	public function getCountryItems(): array
	{
		return collect(Countries::getNames($translator->locale))
			->map(fn (string $title) => new NotTranslate(Strings::firstUpper($title)))
			->all();
	}
}

This will provide all of countries in an array, which can be used in your form factory. For example:

final class CompanyFormFactory
{
	private BasicFormFactory $formFactory;
	
	private CountryFilter $countryFilter;

	public function __construct(BasicFormFactory $formFactory, CountryFilter $countryFilter)
	{
		$this->formFactory = $formFactory;
		$this->countryFilter = $countryFilter;
	}
	
	public function create(): Form
	{
		$form = $this->formFactory->create();
		
		// ...
		
		$form->addSelect('country')
			->setPrompt('form.prompt.select')
			->setItems($this->countryFilter->getCountryItems());
		
		// ...
		
		return $form;
	}
}

You will probably need to edit CountryFilter::getCountryItems to return only some countries (based on your database data), create groups with used and unused countries to make it more user-friendly, append flags inside your select-box, etc. Some of these will be discussed further in other chapters.