{"id":19096670,"url":"https://github.com/stavarengo/travel-sorter","last_synced_at":"2026-04-12T08:40:25.313Z","repository":{"id":42647691,"uuid":"192988809","full_name":"stavarengo/travel-sorter","owner":"stavarengo","description":"This project proposes a solution for the \"Travel Tickets Order\" problem and show real examples of object oriented principles and design patterns on PHP.","archived":false,"fork":false,"pushed_at":"2023-04-19T19:12:33.000Z","size":94,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-03T00:26:21.860Z","etag":null,"topics":["dependency-injection","eulerian-circuit","eulerian-path","inversion-of-control","one-way-flight-problem","phpunit","psr-11","psr-7","travel-tickets-problem","unit-test"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stavarengo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-20T21:20:26.000Z","updated_at":"2020-07-21T13:25:04.000Z","dependencies_parsed_at":"2024-11-09T03:37:34.855Z","dependency_job_id":"ca3e76bf-e6d3-4c5f-a4e1-bd66ee3a31d6","html_url":"https://github.com/stavarengo/travel-sorter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stavarengo%2Ftravel-sorter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stavarengo%2Ftravel-sorter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stavarengo%2Ftravel-sorter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stavarengo%2Ftravel-sorter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stavarengo","download_url":"https://codeload.github.com/stavarengo/travel-sorter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240149841,"owners_count":19755753,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","eulerian-circuit","eulerian-path","inversion-of-control","one-way-flight-problem","phpunit","psr-11","psr-7","travel-tickets-problem","unit-test"],"created_at":"2024-11-09T03:37:26.103Z","updated_at":"2026-04-12T08:40:25.281Z","avatar_url":"https://github.com/stavarengo.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# travel-sorter\n\n## About the project\n\nThis project proposes a solution for the \"Travel Tickets Order\" problem\n([Eulerian Path Problem](https://en.wikipedia.org/wiki/Eulerian_path)). The problem goes like this:\n\u003e You are given a stack of travel tickets that will take you from point A to point B via several stops on the way. \n\u003e All of the tickets are out of order and you don't know where your journey starts, nor where it ends. \n\u003e You must sort the tickets in the right order to complete your journey.\n\nMore then only present a solution for the problem, this project also aims to show real examples of object orient \nprinciples and design patterns on PHP.\n- The application is controlled with configurations files (nothing is hard coded), which make it ease to work in \n  different environments (eg: stage, development, production, etc).\n- It is 100% covered with unit tests.\n- Use [PHP PSR](https://www.php-fig.org/) standards.\n- No frameworks were used (besides [PHPUnit]).\n- Only four dependencies:\n  - [php-di/php-di](https://github.com/PHP-DI/PHP-DI): For dependency injection.\n  - [guzzlehttp/psr7](https://github.com/guzzle/psr7): As [PSR-7] implementation,.  \n  - [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit): For unit test.  \n  - [zendframework/zend-config-aggregator](https://github.com/zendframework/zend-config-aggregator): To merge all of the \n    application configurations together.\n\n## Getting Started\n\n1. Clone the project\n1. Start the project with composer:\n   ```bash\n   $ composer install\n   ```\n1. Start PHP's built-in web server:\n   ```bash\n   $ composer run --timeout=0 serve\n   ```\n1. You can now consume the API on the address http://127.0.0.1:4000\n\nIf you want to start the server using port different of 4000, you can start the server manually:\n```bash\n$ php -S 0.0.0.0:**_YOU_PORT_** -t public/\n```\n\n\u003e ##### Linux users\n\u003e\n\u003e 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\n\u003e affects linux environments. In such scenarios, you will need to start the\n\u003e [built-in web server](http://php.net/manual/en/features.commandline.webserver.php) yourself using the following\n\u003e command:\n\u003e ```bash\n\u003e $ php -S 0.0.0.0:4000 -t public/ public/index.php\n\u003e ```\n\n## Modular Application\n\n- This is a modular application, compound of two modules: \n  1. `App`: Where you can find behaviors not related to an API, such as the [sort algorithm], business logic, etc.\n  1. `Api`: This is where the API resides. This module only has behaviors related to API requests. There you will\n    find the Requests Handlers (AKA controllers), entities used in the API responses, etc.\n\n## Configuration Architecture\n\nThe application behavior can be controlled by external configuration files. These \"external configuration files\"\nresides in folder [`config/autoload`]. So far we only have one file there, but any file inside that folder that ends \nwith `.global.php` will be merged into one single set of configuration.\n\n### Configurations Available\n\nEach module of the application provides its own configuration through a file called `ConfigProvider`. You can see \nthese files at [ConfigProvider of App Module] and [ConfigProvider of Api Module].\n\nAny configuration inside these `ConfigProvider` files can be override by creating the same config entry in a file inside \nthe folder [`config/autoload`]. In fact, that is how we control the routes. Take a look in the file [config/autoload/routes.global.php].\n\n\n## Api Module\n\nThe `travel-sorter` provides an API with one endpoint for sorting your tickets: [`POST /api/sort`].\n\n\u003ca id=\"the-error-response-layout\"\u003e\u003c/a\u003e\n### The Error Response\nAll endpoints, when end in failure, respond with a JSON trying to describe why the error happens (for example, it \nwould fail if you forget to send a required parameter). The following is the JSON used to represent an error response. \n\n```js\n{\n  // This attribute will always be `true`\n  \"error\": true,\n  \n  // A string containing more details about the error.\n  \"detail\": \"Missing the \\\"origin\\\" attribute.\"\n}\n ```\n\n### API endpoints\n\n\u003ca id=\"post-sort\"\u003e\u003c/a\u003e\n#### `POST /api/sort`\nSort a set of tickets. \n\n**URL params**\n\nNone\n\n**Data params**\n\nIt expect that the request body contains a JSON with a list of objects describing each ticket, as following:\n```js\n{\n   // List of tickets to sort.\n  \"tickets\": [\n    {\n      // The kind of the transportation this ticket is related to.\n      // Eg: Train, Flight, Bus\n      // It is required.\n      \"transport\": \"Airport Bus\",\n      \n      // The origin of the trip.\n      // For example: a name of a city, airport, etc.\n      // It is required.\n      \"origin\": \"Barcelona\",\n    \n      // The destiny of the trip.\n      // For example: a name of a city, airport, etc.\n      // It is required.\n      \"destiny\": \"Gerona Airport\",\n    \n      // The seat (if any) where the passenger will sit during the trip.\n      // It is optional.\n      \"seat\": \"7F\",\n      \n      // The boarding gate, if any.\n      // It is optional.\n      \"gate\": \"45B\",\n      \n      // Any extra information related to the tick.\n      // You can use this to say, for example, where the passengers baggage should be left before boarding.\n      // It is optional.\n      \"extra\": \"Baggage will be automatically transferred.\"\n    }\n    \n    // .... MORE ITEMS\n  ]\n}\n```\n\n**Curl example**\n```bash\n$ curl -X POST 'http://127.0.0.1:4000/api/sort' --data-binary '{\n  \"tickets\": [\n    {\"transport\": \"Flight SK455\", \"origin\": \"Gerona Airport\", \"destiny\": \"Stockholm\", \"seat\": \"3A\", \"extra\": \"Baggage drop at ticket counter 344.\"},\n    {\"transport\": \"Airport Bus\", \"origin\": \"Barcelona\", \"destiny\": \"Gerona Airport\"},\n    {\"transport\": \"Flight SK22\", \"origin\": \"Stockholm\", \"destiny\": \"New York JFK\", \"gate\": \"22B\", \"seat\": \"7B\", \"extra\": \"Baggage will we automatically transferred from your last leg.\"},\n    {\"transport\": \"Train 78A\", \"origin\": \"Madrid\", \"destiny\": \"Barcelona\", \"seat\": \"45B\"}\n  ]\n}'\n```\n\n**Success Response**\n\n- `200 - OK`\nIt means that your set of tickets were successfully ordered. The response body will be in the same layout you sent in \nthe request.\n\n**Error Response**\n\nAll error responses will contain a body with [The Error Response Layout].\n- `422 - Unprocessable Entity`: When you make a request without a body.\n- `422 - Unprocessable Entity`: When you request body does not contain the `tickets` attribute.\n- `422 - Unprocessable Entity`: If the `transport` attribute is missing in one of the tickets.\n- `422 - Unprocessable Entity`: If the `origin` attribute is missing in one of the tickets.\n- `422 - Unprocessable Entity`: If the `destiny` attribute is missing in one of the tickets.\n- `405 - Method Not Allowed`: If you do not use the POST method.\n- `400 - Bad Request`: If the set of tickets can not be sorted because they have a missing connection between them.\n- `400 - Bad Request`: If the journey ends in the same place it starts.\n- `500 - Bad Request`: If an unexpected error occurs.\n\n## Composer Scripts\n\nThe `travel-sorter` defines three composer scripts, as follow:\n- **`serve`**: Start PHP's built-in web server on the port 4000.\n- **`test`**: Run all unit tests.\n- **`test-coverage`**: Run all unit tests and generates a code coverage report in HTML format in the folder \n  `test-coverage`. Please note that this functionality is only available when the tokenizer and Xdebug extensions are \n  installed.\n\n\n\n[PHPUnit]: https://phpunit.de/\n[`POST /api/sort`]: #post-sort\n[The Error Response Layout]: #the-error-response-layout\n[sort algorithm]: https://github.com/stavarengo/travel-sorter/tree/master/src/App/TicketsSorter\n[`config/autoload`]: https://github.com/stavarengo/travel-sorter/tree/master/config/autoload\n[config/autoload/routes.global.php]: https://github.com/stavarengo/travel-sorter/blob/master/config/autoload/routes.global.php\n[ConfigProvider of App Module]: https://github.com/stavarengo/travel-sorter/blob/master/src/App/ConfigProvider.php\n[ConfigProvider of Api Module]: https://github.com/stavarengo/travel-sorter/blob/master/src/Api/ConfigProvider.php\n[PSR-7]: https://www.php-fig.org/psr/psr-7/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstavarengo%2Ftravel-sorter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstavarengo%2Ftravel-sorter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstavarengo%2Ftravel-sorter/lists"}