This setup uses docker to spin up php 7.3 container
Requirements:
This step will:
- build images for php container
- install dependencies for the PHP app
Execute:
./run.sh install
Execute:
./run.sh dev
This repository contains solutions for technical tasks for Geomiq interview.
This test is for a Senior PHP engineer position, thus PHP was chosen for the solution
- Geomiq uses Laravel internally
- ...Laravel however introduces too much overhead for such a small application. Which is why I chose Lumen as it's significantly smaller than Laravel (performance overhead, cognitive overhead), uses Laravel components - mainly the console components which help out a ton with the tasks presented.
- If I didn't use Lumen I would have to handle CLI input myself or through the usage of another library. Lumen does not have much of an overhead in comparison to such libraries at the same time saving me time; thus the choice.
To execute the command make sure the container is running, then ssh into it
docker-exec -it geomiq-service /bin/bash
Then run the command like
php artisan php artisan features:map-to-json "string-to-parse"
You can also execute the tests, like so:
./vendor/bin/phpunit
Since Laravel nor Lumen implements PSR7 we can:
- Either Fetch string from the request input and call FeaturesListBuilder::build with the string
- Implement PSR7 in commands and controllers, in which case the handler is the same - we don't have to change anything, just pass the handler to router
If we are following RESTful API design could be as follows (swagger docs):
swagger: "2.0"
info:
description: "Documentation for BookLovr API"
version: "1.0.0"
title: "Geomiq Features API"
host: "booklovr.local"
basePath: "/"
paths:
/geofeatures:
get:
tags:
- "geo"
summary: "Get mapped GeoFeatures"
description: ""
operationId: "getGeoFeatures"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: query
name: input_string
type: string
description: Input string to do mapping for
responses:
200:
description: OK
schema:
$ref: "#/definitions/MappedGeofeatures"
definitions:
MappedGeofeatures:
type: "object"
properties:
data:
type: "object"
The following query can be used to produce a view as desired:
SELECT DISTINCT users.id,
users.name,
CASE
WHEN roles.name is null THEN 'buyer'
ELSE roles.name
END as role,
users.email,
profiles.company_name,
users.created_at AS registered_on,
users.last_login AS last_login
FROM users
LEFT JOIN profiles ON (users.id = profiles.user_id)
LEFT JOIN roles ON (roles.id = profiles.id)
LEFT JOIN model_has_roles ON (users.id = model_has_roles.model_id)
ORDER BY profiles.company_name DESC
How would I implement this for daily use:
- Using Eloquent query builder:
- query builder has little to no overhead in comparison to raw PDO statements. If we cannot use models I would at least try to convince the team to go for Repository pattern & QueryBuilder inside of it generating the query
- I would inject the repository to the command via constructor injection
- I would display the results using inbuild Lumen/Laravel CLI output table() function
This has the following benefits instead of using prepared statements ourselves:
- eloquent does actually use prepared statements in the background, however using QueryBuilder will add a layer of abstraction and security. There's less of a chance engineer that works on it next time will introduce a security issue, eg. SQL injection
- less cognitive load on the engineers, as the query is written semantically with the builder pattern that QueryBuilder implements
- more easilly testable, as QueryBuilder is easilly mockable
-
The first word in the string is a key name (as called in the code: feature name)
-
Assumptions about values:
⋅⋅1. N elements from the feature value until next string are treated as feature values
⋅⋅2. If there are more than 1 values for the feature name - first value is treated as a key
Eg.
feature_name-1-2=3-second_one%2.21
Translates into
{ 'data' : { 'features' => [ { 'id': '1', 'feature_name': [2, 3] } ], second_one: 2.21 } }
It's quite difficult to sanitize the input string, considering I do not have enough information to assume it's format.
- until further instructions about potential formats are provided, or for example metadata to validate the mapping with, the solution will never be too robust as we do not have a way to validate
- given the beforementioned we could easilly enhance the solution by implementing, for example, factory pattern for sanitizers
- To be honest I am not that great with Regexp, sadly my solution does not work with ScientificNotation for floats. There's a test for it, but it's failing right now. Given the sanitization regexp to be improved the test would pass as well 😅