Symfony command line for getting weather using Console Component in a standalone mode
Create a simple weather command line with Symfony Console Component without the need of a Symfony project.
Symfony Console Component in standalone mode
You can create a command within a Symfony Project easily by requiring symfony/console component and then creating a class that extends Symfony\Component\Console\Command\Command but does this means that you have to have a Symfony project in order to create a command line with the console component ? Of course No , one of the cool things that Symfony does is that it provides us with components that we can use without the need of the framework itself. So you can use the component in a standalone way and that’s what we are going to together.
1. Create a new folder and a new php file
You simply need to create a folder let’s call it StandAloneCommand then within create an src folder and finally create a new php file, let’s name it weather.php so we should have this tree : StandAloneCommand > src > weather.php
Then open a Terminal , go to StandAloneCommand folder and execute the following command :
composer require symfony/console symfony/http-clientThis will create a vendor folder for you and download two Symfony components needed for our command: symfony/console is for the command line , and symfony/http-client is to get the weather forecast from an external API.
You should have this tree by now :
2. Create a simple Hello world command
Edit your src/weather.php file and put in this code :
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
require __DIR__ . '/../vendor/autoload.php';
(new SingleCommandApplication())
->setName('app:weather')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$output->writeln('Hello weather !');
return Command::SUCCESS;
})
->run(); The require line is what will allow us to use the components that we have download using composer.
The rest of the code snippet defines a single command application and run it.
To test your code , open a terminal, type in php weather.php and you should see the greeting message :
$ php weather.php
Hello weather !3. Add arguments, options and test the command line
Now let’s add arguments and options to specify them for the forecast:
Edit your src/weather.php file and put in this code :
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
require __DIR__ . '/../vendor/autoload.php';
(new SingleCommandApplication())
->setName('app:weather')
->addArgument('lat', InputArgument::REQUIRED)
->addArgument('lng', InputArgument::REQUIRED)
->addOption('days','d', InputOption::VALUE_OPTIONAL, 'days', 7)
->setCode(function (InputInterface $input, OutputInterface $output): int {
// 1. getting inputs ( arguments , options ... )
$lat = (float)$input->getArgument('lat');
$lng = (float)$input->getArgument('lng');
$days = (int)$input->getOption('days');
$output->writeln(sprintf(" lat: %s, lng:%s , days:%s ", $lat, $lng, $days ));
return Command::SUCCESS;
})
->run()We have just added two required arguments latitude and longitude for the city concerned by the forecast and an optional number of forecast days.
If we type in php weather.php 45.2 8.3 --days 4 we should be seeing this output in our console :
$ php weather.php 45.2 8.3 --days 4
lat: 45.2, lng:8.3 , days:4 4. Ask a question and propose auto-complete values
To ask a question we are going to use the question helper , create a new Question and then provide autocomplete values :
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\SingleCommandApplication;
require __DIR__ . '/../vendor/autoload.php';
(new SingleCommandApplication())
->setName('app:weather')
->addArgument('lat', InputArgument::REQUIRED)
->addArgument('lng', InputArgument::REQUIRED)
->addOption('days','d', InputOption::VALUE_OPTIONAL, 'days', 7)
->setCode(function (InputInterface $input, OutputInterface $output): int {
....
// asking question about temperature measurement unit
$helper = $this->getHelper('question');
$question = new Question("Do you prefer temperature in fahrenheit or celsius ? \n");
$question->setAutocompleterValues(['fahrenheit', 'celsius']);
$temperatureUnit = $helper->ask($input, $output, $question);
return Command::SUCCESS;
})
->run();If we run our command we should see our question being asked and when we type c or f as answer Symfony will autocomplete for us celsius or fahrenheit:
5.Get the weather forecast from an external API
First we will show a nice progress bar to say to the user that we are performing a time consuming task.
Then we will use the http-client component to get the forecast from an external API by sending a GET request to this endpoint : https://api.open-meteo.com/v1/forecast .
A query string that contains relevant informations such as lat, lng , temperature unit … should be provided to out API.
When we are done getting the response and parsing the JSON to a PHP array we stop the progress bar.
<?php
(new SingleCommandApplication())
->setName('app:weather')
->addArgument('lat', InputArgument::REQUIRED)
->addArgument('lng', InputArgument::REQUIRED)
->addOption('days','d', InputOption::VALUE_OPTIONAL, 'days', 7)
->setCode(function (InputInterface $input, OutputInterface $output): int {
.....
// showing a progress bar
$progressBar = new ProgressBar($output);
$progressBar->start();
sleep(1);
$progressBar->setProgress(50);
// fetching data from forecast api
$response = $this->httpClient->request('GET',
'https://api.open-meteo.com/v1/forecast',
['query' => [
'latitude' => $lat,
'longitude' => $lng,
'daily' => 'temperature_2m_max,temperature_2m_min',
'timezone' => 'Europe/Paris',
'forecast_days' => $days,
'temperature_unit' => $temperatureUnit,
]]
)->toArray();
// stopping progress bar
$progressBar->setProgress(100);
$progressBar->finish();
$output->writeln('');
return Command::SUCCESS;
})
->run();6. Display the forecast as a table
Finally we will construct a table from the result of our http call and we will display it to the user :
(new SingleCommandApplication())
->setName('app:weather')
->addArgument('lat', InputArgument::REQUIRED)
->addArgument('lng', InputArgument::REQUIRED)
->addOption('days','d', InputOption::VALUE_OPTIONAL, 'days', 7)
->setCode(function (InputInterface $input, OutputInterface $output): int {
...
// displaying data as table
$table = new Table($output);
$table->setHeaders(['Day', 'Temperature Min', 'Temperature Max']);
$rows = [];
foreach ($response['daily']['time'] as $key => $date) {
$rows[] = [
$date,
$response['daily']['temperature_2m_min'][$key],
$response['daily']['temperature_2m_max'][$key]
];
}
$table->setRows($rows);
$table->render();
return Command::SUCCESS;
})
->run();Our final code should look like this :
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\SingleCommandApplication;
use Symfony\Component\HttpClient\HttpClient;
require __DIR__ . '/../vendor/autoload.php';
(new SingleCommandApplication())
->setName('app:weather')
->addArgument('lat', InputArgument::REQUIRED)
->addArgument('lng', InputArgument::REQUIRED)
->addOption('days','d', InputOption::VALUE_OPTIONAL, 'days', 7)
->setCode(function (InputInterface $input, OutputInterface $output): int {
// getting inputs ( arguments , options ... )
$lat = (float)$input->getArgument('lat');
$lng = (float)$input->getArgument('lng');
$days = (int)$input->getOption('days');
$output->writeln(sprintf(" lat: %s, lng:%s , days:%s ", $lat, $lng, $days ));
// asking question about temperature measurement unit
$helper = $this->getHelper('question');
$question = new Question("Do you prefer temperature in fahrenheit or celsius ? \n");
$question->setAutocompleterValues(['fahrenheit', 'celsius']);
$temperatureUnit = $helper->ask($input, $output, $question);
$output->writeln(sprintf(" temperature will be in :%s ", $temperatureUnit ));
// showing a progress bar
$progressBar = new ProgressBar($output);
$progressBar->start();
sleep(1);
$progressBar->setProgress(50);
// fetching data from forecast api
$response = (HttpClient::create())->request('GET',
'https://api.open-meteo.com/v1/forecast',
['query' => [
'latitude' => $lat,
'longitude' => $lng,
'daily' => 'temperature_2m_max,temperature_2m_min',
'timezone' => 'Europe/Paris',
'forecast_days' => $days,
'temperature_unit' => $temperatureUnit,
]]
)->toArray();
// stopping progress bar
$progressBar->setProgress(100);
$progressBar->finish();
$output->writeln('');
// displaying data as table
$table = new Table($output);
$table->setHeaders(['Day', 'Temperature Min', 'Temperature Max']);
$rows = [];
foreach ($response['daily']['time'] as $key => $date) {
$rows[] = [
$date,
$response['daily']['temperature_2m_min'][$key],
$response['daily']['temperature_2m_max'][$key]
];
}
$table->setRows($rows);
$table->render();
return Command::SUCCESS;
})
->run();Voila ! we are done.
You can checkout the code in my github repo : https://github.com/zizoujab/WeatherCommandStandAlone
This article is available as a video on my Youtube channel :
Test your knowledge
As a bonus for ending the article here are some questions that will help you memorize some facts seen above and other facts about Symfony Console Component:







