#StackBounty: #php #object-oriented #url Parse a clean slug URL

Bounty: 50

I have to implement a “user-friendly” URL for an employment website, the URL /jobs{parameters?} may be decomposed up to three searching criteria separated by -, each criterion is separated by ~. The parameters string has the following structure
-keywords-employmentTypes-locations, keywords and employmentTypes are optional, location is mandatory unless there are no parameters, the route should handle all those cases:

/jobs

/jobs-canada     /jobs-canada~paris~usa

/jobs-full_time-canada  /jobs-full_time~part_time-canada~paris
/jobs-engineer-canada  /jobs-engineer~c++-canada~paris

/jobs-engineer-full_time-canada  /jobs-engineer~c++-full_time-canada~paris

The following class parses the string parameters and fill all criteria in variables:

class JobUrlBuilder
{
    const DEFAULT_LOCATION = 'france';

    public $keywords;
    public $employmentTypes;
    public $locations;

    public $allEmploymentTypes;

    function __construct($allEmploymentTypes = [])
    {
        $this->setAllEmploymentTypes($allEmploymentTypes);
    }

    /**
     * @param string $parameters
    */
    public function setParameters($parameters)
    {
        $parameters = explode('-', ltrim($parameters, '-'));
        if (empty($parameters[0])) {
            array_shift($parameters);
        }

        if (count($parameters) === 3) {
            $this->keywords  = $parameters[0];
            $this->employmentTypes = $parameters[1];
            $this->locations = $parameters[2];
        } else if (count($parameters) === 2) {
            $areEmploymentTypes = $this->areEmploymentTypes(explode('~', $parameters[0]));

            $this->keywords  = $areEmploymentTypes ? null : $parameters[0];
            $this->employmentTypes = $areEmploymentTypes ? $parameters[0] : null;
            $this->locations = $parameters[1];
        } else if (count($parameters) === 1) {
            $this->keywords  = null;
            $this->employmentTypes = null;
            $this->locations = $parameters[0];
        } else {
            $this->keywords  = null;
            $this->employmentTypes = null;
            $this->locations = self::DEFAULT_LOCATION;
        }

        $this->keywords = $this->keywords ? explode('~', $this->keywords) : [];
        $this->employmentTypes = $this->employmentTypes ? explode('~', $this->employmentTypes) : [];
        $this->locations = $this->locations ? explode('~', $this->locations) : [];

        return $this;
    }

    /**
    * @param array $items
    */
    public function areEmploymentTypes($items)
    {
        $jobTypes = array_map(function ($v)
        {
            return mb_strtolower($v);
        }, $this->allEmploymentTypes);

        return ! array_diff($items, $jobTypes);
    }

    /**
    * @param array $employmentTypes
    */
    public function setAllEmploymentTypes($employmentTypes)
    {
        $this->allEmploymentTypes = $employmentTypes;
    }

    public function hasDefaultLocation()
    {
        return in_array(self::DEFAULT_LOCATION, $this->locations);
    }
}

I don’t like this code (spaghetti code, no separation of concerns…), I think there is a better implementation. I’m also open to other URL structures, I know I can simply use input parameters and call it a day, but I should implement a clean URL. I have added unit tests to handle all cases, I think it might help you understand the code.


Get this bounty!!!

Leave a Reply