Build File Converting Application with Symfony
Time to read: 10 minutes
Build File Converting Application with Symfony
Data plays a central role in everyday life. Whether it’s in generating reports, populating databases, exchanging information, or analytics — you are very likely to interact with data in varying formats and to find yourself converting between them.
In this tutorial, I will show you how to build a Symfony application that converts between four popular data formats — CSV, JSON, SQL, and XLSX. In addition to this, I will show you some Symfony features that will allow you to seamlessly add new formats to your application without touching your existing code.
Prerequisites
To follow this tutorial, you will require the following.
- A basic understanding of and familiarity with PHP and Symfony
- PHP 8.2 or above
- Composer globally installed
- The Symfony CLI
- A JavaScript package manager (npm will be used in this tutorial)
Create a new Symfony project
Create a new Symfony project and change into the new project directory using the following commands.
Next, add the project's dependencies using the following commands.
Docker won’t be used in this tutorial, so press the "n" key when prompted to create a docker.yaml file.
While installing the Flowbite-bundle, you will see the warnings about executing recipes from the "contrib" repository. Press the y key on both occasions.
Here’s what each dependency is for:
- Doctrine: This package will be used to handle database-related activity
- Encore: This will be used to compile CSS and JS assets
- Form: This bundle simplifies the process of building, rendering and managing HTML forms in a Symfony application
- Flowbite-bundle: This bundle provides a Tailwind CSS-based form theme for your application forms
- Mime: This bundle provides utilities related to MIME types. In this tutorial, you will use it to get more information on the uploaded file.
- Maker: This bundle helps with the auto-generation of code associated with controllers, forms, and entities
- PHPSpreadsheetBundle: This bundle integrates your application with the PhpSpreadsheet productivity library, allowing you to read and create CSV and XLSX files
- Twig: This bundle allow you to use Twig templating in your application
- Validator: This bundle helps with validation of your Symfony form
Set the required environment variables
For this project, you will need two environment variables — one for the database URL and, another for the location where converted files should be written to.
Add the following variables to the end of .env located at the root of the project directory.
Next, create a new folder named output in the root directory of the project. This is the folder referred to in the .env file in OUTPUT_FOLDER
.
For this tutorial, SQLite will be used for the database. The database will be saved to a file named data.db in the var folder. Create this file using the following command.
Create the required entity
Your application will have a single entity named Conversion
. This entity will hold relevant information pertaining to the file uploaded for conversion, as well as the file generated on a successful conversion. Create it using the following command.
You will be prompted with the following question.
Here, press Enter to complete the creation process. This process will create an entity class and a repository class for database related activity pertaining to the ConversionResult
entity.
Next, open the newly created src/Entity/Conversion.php and update it to match the following code.
Apart from the usual getters and setters for the entity, a function named readableFileSize()
was declared. It takes the size of a file as an integer and returns the size in a readable format e.g., ‘9.6KB’. This function is used to set the values of $uploadedFileSize
and $convertedFileSize
as readable strings instead of the raw integer value.
Next, add the function below to src/Repository/ConversionRepository.php (also created by the previous command), to get the list of conversion results in descending order.
With these changes in place, provision your database using the following commands.
Create helper services
The next step is to create some services to help with the process of format conversion. These services will fall into three categories as follows:
- Reader: These services read files in a specified format and create an intermediary object for the converter.
- Writer: These services write the content of an intermediary object into a file of a particular format.
- Converter: These services combine the reader and writer for a particular format, giving one the ability to read and write files in that format.
As mentioned at the start of the tutorial, reading, writing, and conversion will be between the CSV, JSON, SQL, and XLSX formats. While each format will have its own converter, the CSV and XLSX formats can use the same reader and writer since the code for reading and writing in those formats is identical.
In the src folder, create a new folder named Helper. This is where the above mentioned services will live.
Next, in the src/Helper folder, create three new folders, named Converter, Reader, and Writer, to hold the respective services.
Create the reader services
In the Reader folder, create a new file named FileContent.php and add the following code to it.
This object takes two arrays — one for the data keys, and another for the data itself. If either of these arrays are empty, an exception is thrown.
At the moment the FileReadException
class does not exist, so in the Reader folder, create a new file named FileReadException.php. Then, add the following to it.
Next, create a new file named AbstractReader.php in the Readerfolder and add the following code to it.
All the readers you will create extend this class and override the read()
function based on the peculiarities of the data format.
Create a JSON reader
Next, in the src/Helper/Reader folder, create a new file named JsonReader.php with the following code.
Create a Spreadsheet reader
Next, in the src/Helper/Reader folder, create a new file named SpreadsheetReader.php with the following code.
Create an SQL reader
Next, in the src/Helper/Reader folder, create a new file named SQLReader.php with the following code.
Create the writer services
The writer services will follow the same hierarchy as the reader services. An abstract writer will be used to declare the fields and a base function, while the child writers will override the base function to provide the required functionality for the specified format.
In the writer folder, create a new file named AbstractWriter.php,and add the following code to it.
Each writer will be initialised by providing the location for the file to be written to and a name for the file. By overriding the write()
function, they will be able to write the content in the specified format.
Create a JSON writer
In the writer folder, create a new file named JSONWriter.php and add the following code to it.
Create a spreadsheet writer
In the writer folder, create a new file named SpreadsheetWriter.php and add the following code to it.
Because the SpreadsheetWriter can be used to write either CSV or XLSX files, the constructor function is overridden to allow you specify whether or not the writer will be writing a CSV file or not.
Create an SQL writer
In the writer folder, create a new file named SQLWriter.php and add the following code to it.
Create the converter services
The converter services are the glue between the reader and writer services.
The strategy for implementation will differ slightly from that which was used in creating the readers and writers. Instead of an abstract class being used, an interface will be defined — specifying the methods which all the converters must implement.
This is because when you are creating the service that determines which converter to use, your application will be more flexible if it interacts with an interface rather than a concrete implementation.
In the Converter folder, create a new file named FileConverterInterface.php and add the following code to it.
To implement this interface, four functions must be declared:
supports(string $fileExtension)
: Given a file extension, a converter must return a boolean indicating whether or not it can handle files bearing that extension.getReader()
: A converter must return a reader which can be used to generate theFileContent
.getWriter(string $saveLocation, string $fileName)
: A converter must return a writer which will write the file in the expected format, to the provided location, and save it with the specified name.getSupportedFormat()
: A converter must return a string corresponding to the format it supports for conversion.
Pay attention to the AutoconfigureTag
attribute declared on the FileConverterInterface
. This applies the ‘app.converter’
tag to all services that implement the interface.
Create a CSV converter
In the Converter folder, create a new file named CSVConverter.php and add the following code to it.
Create a JSON converter
In the Converter folder, create a new file named JsonConverter.php, and add the following code to it.
Create an SQL converter
In the Converter folder, create a new file named SQLConverter.php and add the following code to it.
Create an XLSX converter
In the Converter folder, create a new file named XLSXConverter.php and add the following code to it.
Before proceeding, create a new exception which will be thrown if the conversion service cannot find an appropriate converter. In the Converter folder, create a new file named UnsupportedFormatException.php, and add the following code to it.
Create the file conversion service
In the src folder, create a new folder named Service, and in that new folder create a new file named ConversionService.php. Then, add the following code to the newly created file.
This service has three fields:
converters
: This is an array of objects which implement theFileConverterInterface
you created earlieravailableFormats
: This is an array containing the file formats your application currently supportssaveLocation
: This is the location all converted files should be written to. Using the Autowire attribute, you are able to inject the location to the output folder you created earlier
In addition to receiving the location of the output folder as a constructor parameter, this service also retrieves an iterable named converters
which it converts to an array and saves in the converters
array. Using the AutowireIterator
attribute, all the services that implement the FileConverterInterface
are injected as an array into the service.
Create a form for uploading files
Create a new form object using the following command:
Respond to the resulting prompt as shown below.
Open the newly created src/Form/ConversionType.php and update it to match the following code.
Based on the implementation of the buildForm()
function, the form will have the following fields:
- A drop down named
convertedFileFormat
containing all the currently supported data formats - A text field named
nameToSaveAs
which takes a name with which to save the file - A file input field which allows the user to select a file to convert. This field is not mapped to the
Conversion
entity, and has a maximum file limit of ten megabytes. - A save button which submits the provided information for further processing
Create a controller
Next, create a new controller to handle incoming requests using the following command.
Open the newly created file in src/Controller/ConversionController.php and update its content to match the following.
The application will have four endpoints as shown below.
By default, you want the index page to return the conversion form. At the moment, it returns the default Symfony welcome page. To fix that, add the following route in config/routes.yaml.
With that, the backend of the application is in place. The next thing is to create (or update) the templates which will be used in rendering the application’s views.
Create the view templates
The controller you created earlier renders and returns some views based on templates you have not yet created or updated. So, start by updating the code in templates/base.html.twig to match the following.
In the base template, you create an entrypoint for your encore assets, and import the Inter font which will be used in the application. In addition to that, you created a navigation menu which allows you to switch between the index page and conversion history.
Next, create a template to render the conversion form. Update the code in templates/conversion/index.html.twig to match the following.
This template renders each row of the form and wraps it in a div to position it in the middle of the screen. You also made provision for error messages in the event that invalid data was submitted.
Finally, create a new file named history.html.twig in the templates/conversion folder and add the following code to it.
With the templates in place, you can install Tailwind CSS and compile your webpack assets.
Install Tailwind and Flowbite
Tailwind and Flowbite will be used to beautify the display of your application. Since you have already installed the webpack bundle, install Tailwind and Flowbite using the following commands
Next, at the root of the project folder, update your webpack.config.js file to match the following code.
Now, update tailwind.config.js to match the following code.
Next, import the Tailwind directives, by updating the code in assets/styles/app.css to match the following code.
Also, make sure that the app.css file is imported in assets/app.js.
Next, add the Flowbite form theme to Twig. To do this, open config/packages/twig.yaml and update the code to match the following.
Finally, compile your webpack assets using the following command.
Run the application using the following command.
By default, the application runs on port 8000. Navigate to that port in your browser and your application will run as shown below.
Conclusion
There you have it! Not only have you built an application capable of handling conversions for your everyday activities, you’ve structured it in a way that allows for easy addition of new formats in future. You can review the final codebase for this article on GitHub, should you get stuck at any point. I’m excited to see what else you come up with.
The journey doesn’t stop here however. In a subsequent tutorial, I will show you how Symfony can make the process of adding new converters even easier. Until next time, make peace not war✌🏾
Joseph Udonsak is a software engineer with a passion for solving challenges — be it building applications, or conquering new frontiers on Candy Crush. When he’s not staring at his screens, he enjoys a cold beer and laughs with his family and friends. Find him at LinkedIn, Medium, and Dev.to.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.