This project proposes a solution for the "Travel Tickets Order" problem (Eulerian Path Problem). The problem goes like this:
You are given a stack of travel tickets that will take you from point A to point B via several stops on the way. All of the tickets are out of order and you don't know where your journey starts, nor where it ends. You must sort the tickets in the right order to complete your journey.
More then only present a solution for the problem, this project also aims to show real examples of object orient principles and design patterns on PHP.
- The application is controlled with configurations files (nothing is hard coded), which make it ease to work in different environments (eg: stage, development, production, etc).
- It is 100% covered with unit tests.
- Use PHP PSR standards.
- No frameworks were used (besides PHPUnit).
- Only four dependencies:
- php-di/php-di: For dependency injection.
- guzzlehttp/psr7: As PSR-7 implementation,.
- phpunit/phpunit: For unit test.
- zendframework/zend-config-aggregator: To merge all of the application configurations together.
- Clone the project
- Start the project with composer:
$ composer install
- Start PHP's built-in web server:
$ composer run --timeout=0 serve
- You can now consume the API on the address http://127.0.0.1:4000
If you want to start the server using port different of 4000, you can start the server manually:
$ php -S 0.0.0.0:**_YOU_PORT_** -t public/
On PHP versions prior to 7.1.14 and 7.2.2, this command might not work as expected due to a bug in PHP that only affects linux environments. In such scenarios, you will need to start the built-in web server yourself using the following command:
$ php -S 0.0.0.0:4000 -t public/ public/index.php
- This is a modular application, compound of two modules:
App
: Where you can find behaviors not related to an API, such as the sort algorithm, business logic, etc.Api
: This is where the API resides. This module only has behaviors related to API requests. There you will find the Requests Handlers (AKA controllers), entities used in the API responses, etc.
The application behavior can be controlled by external configuration files. These "external configuration files"
resides in folder config/autoload
. So far we only have one file there, but any file inside that folder that ends
with .global.php
will be merged into one single set of configuration.
Each module of the application provides its own configuration through a file called ConfigProvider
. You can see
these files at ConfigProvider of App Module and ConfigProvider of Api Module.
Any configuration inside these ConfigProvider
files can be override by creating the same config entry in a file inside
the folder config/autoload
. In fact, that is how we control the routes. Take a look in the file config/autoload/routes.global.php.
The travel-sorter
provides an API with one endpoint for sorting your tickets: POST /api/sort
.
All endpoints, when end in failure, respond with a JSON trying to describe why the error happens (for example, it would fail if you forget to send a required parameter). The following is the JSON used to represent an error response.
{
// This attribute will always be `true`
"error": true,
// A string containing more details about the error.
"detail": "Missing the \"origin\" attribute."
}
Sort a set of tickets.
URL params
None
Data params
It expect that the request body contains a JSON with a list of objects describing each ticket, as following:
{
// List of tickets to sort.
"tickets": [
{
// The kind of the transportation this ticket is related to.
// Eg: Train, Flight, Bus
// It is required.
"transport": "Airport Bus",
// The origin of the trip.
// For example: a name of a city, airport, etc.
// It is required.
"origin": "Barcelona",
// The destiny of the trip.
// For example: a name of a city, airport, etc.
// It is required.
"destiny": "Gerona Airport",
// The seat (if any) where the passenger will sit during the trip.
// It is optional.
"seat": "7F",
// The boarding gate, if any.
// It is optional.
"gate": "45B",
// Any extra information related to the tick.
// You can use this to say, for example, where the passengers baggage should be left before boarding.
// It is optional.
"extra": "Baggage will be automatically transferred."
}
// .... MORE ITEMS
]
}
Curl example
$ curl -X POST 'http://127.0.0.1:4000/api/sort' --data-binary '{
"tickets": [
{"transport": "Flight SK455", "origin": "Gerona Airport", "destiny": "Stockholm", "seat": "3A", "extra": "Baggage drop at ticket counter 344."},
{"transport": "Airport Bus", "origin": "Barcelona", "destiny": "Gerona Airport"},
{"transport": "Flight SK22", "origin": "Stockholm", "destiny": "New York JFK", "gate": "22B", "seat": "7B", "extra": "Baggage will we automatically transferred from your last leg."},
{"transport": "Train 78A", "origin": "Madrid", "destiny": "Barcelona", "seat": "45B"}
]
}'
Success Response
200 - OK
It means that your set of tickets were successfully ordered. The response body will be in the same layout you sent in the request.
Error Response
All error responses will contain a body with The Error Response Layout.
422 - Unprocessable Entity
: When you make a request without a body.422 - Unprocessable Entity
: When you request body does not contain thetickets
attribute.422 - Unprocessable Entity
: If thetransport
attribute is missing in one of the tickets.422 - Unprocessable Entity
: If theorigin
attribute is missing in one of the tickets.422 - Unprocessable Entity
: If thedestiny
attribute is missing in one of the tickets.405 - Method Not Allowed
: If you do not use the POST method.400 - Bad Request
: If the set of tickets can not be sorted because they have a missing connection between them.400 - Bad Request
: If the journey ends in the same place it starts.500 - Bad Request
: If an unexpected error occurs.
The travel-sorter
defines three composer scripts, as follow:
serve
: Start PHP's built-in web server on the port 4000.test
: Run all unit tests.test-coverage
: Run all unit tests and generates a code coverage report in HTML format in the foldertest-coverage
. Please note that this functionality is only available when the tokenizer and Xdebug extensions are installed.