4

I have an app, presumably an API, which I am working on. The app returns in JSON format for the requested resources. So I have a project management application, where the structure is similar to what is below:

  • Projects
    • Payments
    • Issues
      • Discussion
  • Users

Now the API will give calls to:

/projects                List all the projects
/project                 List all the projects (alias)
/projects/ID/issues      List all the issues of this project
/project/ID/issues       List all the issues of this project (alias)
/projects/ID/issue       List all the issues of this project (alias)
/project/ID/issue        List all the issues of this project (alias)

And so on. Now the problem for me is, I would be using switch ($request) for this and I have crazy case statements like below:

<?php
  switch ($request) {
    case '/projects':
    case '/project':
      # code...
      break;

    case '/projects/ID/issues':
    case '/project/ID/issues':
    case '/projects/ID/issue':
    case '/project/ID/issue':
      # code...
      break;
  }

I hope you understood the problem. Think about the number of cases for the discussion part. It would be exponentially higher. That would go by combination of the 3 values, which will come to 2 to the power of 3 (23) which comes around 8 case statements.

Is there a best way to reduce this? This is my first time in Stack Overflow. Thanks in advance.

3 Answers3

1

Defining the routes

I'd recommend using preg_match for this kind of stuff. Start by making an array, with route names as keys and regular expressions as values:

$routes = [
    'projects' => '/\/projects?$/',
    'payments' => '/\/projects?\/([0-9]+)\/payments?$/',
    'issues' => '/\/projects?\/([0-9]+)\/issues?$/',
    'discussions' => '/\/projects?\/([0-9]+)\/issues?\/([0-9]+)\/discussions?$/',
    'users' => '/\/users?$/
];

Now check which route is matched by your url:

function getRoute($url, $routes) {
    foreach ($routes as $name => $regex) {
        if (preg_match($regex, $url)) {
            return $name;
        }
    }

    // return false, or throw an exception if no route has been found
}

And now you can simply go by route name in your switch:

switch (getRoute($request, $routes)) {
    case 'projects':
        ...
    case 'payments':
        ...
    case 'issues':
        ...
    case 'discussions':
        ...
    case 'users':
        ...
}

I believe this is the most elegant solution for what you're trying to do. You can easily export the routes array to an external config if you wish. You are also avoiding any modifications of the url.

Fetching the parameters (id's)

You could expand the getRoute function to not only return the route name, but also return the parameters:

function getRoute($url, $routes) {
    foreach ($routes as $name => $regex) {
        if (preg_match($regex, $url, $matches) {
            // removes the first match which is the whole url
            array_shift($matches);

            return ['route' => $name, 'params' => $matches];
        }
    }
}

The above will return an array containing the matched route name as well as its params. Here's an example:

$route = getRoute('/project/13/issues/14/discussions', $routes);

// $route = [
//     'route' => 'discussions', 
//     'params' => [0 => '13', 1 => '14']
// ];

Just don't forget to change your switch statement to:

$route = getRoute($request, $routes);

switch($route['route']) {
    ...
}

Let me know if you need any further explanation.

Kuba Birecki
  • 2,926
  • 1
  • 13
  • 16
1

You can reduce it by checking in the first attempt. i.e., check for the plural or singular form, and replace it with one single form.

<?php
  $request = replace(array("projects", "project"), "project", $request);
  $request = replace(array("issues", "issue"), "issue", $request);
  $request = replace(array("users", "user"), "user", $request);
  $request = replace(array("discussions", "discussion"), "discussion", $request);

And later check in the switch () case for just the singular form:

<?php
  switch ($request) {
    case '/project':
      # code...
      break;

    case '/project/ID/issue':
      # code...
      break;
  }

I know this would be a repetitive task, but I guess if you don't want to use any pluralisation methods, this is better. Hope this "quick hack" is helpful. Since you don't have more than three or four variables, this is better.

Note: The main reason I gave this answer is for Simplicity and for not using RegEx.

Praveen Kumar Purushothaman
  • 164,888
  • 24
  • 203
  • 252
-1

A similar approch to Praveen Kumars is to parse the request part by part and handling them accordingly. You could split it using $requestParts = explode('/',$request); then you go:

if (($requestParts[0]=='project')||($requestParts[0]=='projects')) {
    handleProjectRequest($requestParts);
}
elseif (($requestParts[0]=='user')||($requestParts[0]=='users')) {
    handleUserRequest($requestParts);
}
//...and so on...

Your subfunctions might then look like this:

function handleProjectRequest(&$requestParts) {
    if ($requestParts[1]=='ID') { //seems to be the only case?
        if (($requestParts[2]=='issue')||($requestParts[2]=='issues')) {
            //do your magic
        }
    }
}

This will help you get rid of that giant switch statement and also organize your code pretty good.

In an advanced way, you could define the plural and singular forms for each case in an array, like this: $projectRequests=['project,projects']; and check them later:

if (in_array($requestParts[0],$projectRequests)) {
    handleProjectRequest($requestParts);
}

The advantage of this approach is, that you could centrally define all the possible parts of each request.

ahuemmer
  • 1,653
  • 9
  • 22
  • 29
  • Downvoter, please explain why. I see, there are better answers, unquestionably. But IMHO, what I wrote isn't wrong and does also answer the question. So please tell me where my mistake was, so I can improve. Thanks! – ahuemmer May 04 '16 at 18:22