/**
  * Saves a new or existing route.
  *
  * @param array       $urlParts The URL as defined by the user. This is an array where each element is either a
  *                              string or an array containing the name of a subpattern and the subpattern.
  * @param string      $template The template to route matching URLs to.
  * @param int|null    $routeId  The route ID, if editing an existing route.
  * @param string|null $locale
  *
  * @throws Exception
  * @return RouteRecord
  */
 public function saveRoute($urlParts, $template, $routeId = null, $locale = null)
 {
     if ($routeId !== null) {
         $routeRecord = RouteRecord::model()->findById($routeId);
         if (!$routeRecord) {
             throw new Exception(Craft::t('No route exists with the ID “{id}”.', array('id' => $routeId)));
         }
     } else {
         $routeRecord = new RouteRecord();
         // Get the next biggest sort order
         $maxSortOrder = craft()->db->createCommand()->select('max(sortOrder)')->from('routes')->queryScalar();
         $routeRecord->sortOrder = $maxSortOrder + 1;
     }
     // Compile the URL parts into a regex pattern
     $urlPattern = '';
     $urlParts = array_filter($urlParts);
     $subpatternNameCounts = array();
     foreach ($urlParts as $part) {
         if (is_string($part)) {
             // Escape any special regex characters
             $urlPattern .= StringHelper::escapeRegexChars($part);
         } else {
             if (is_array($part)) {
                 // Is the name a valid handle?
                 if (preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $part[0])) {
                     $subpatternName = $part[0];
                     // Make sure it's unique
                     if (isset($subpatternNameCounts[$subpatternName])) {
                         $subpatternNameCounts[$subpatternName]++;
                         // Append the count to the end of the name
                         $subpatternName .= $subpatternNameCounts[$subpatternName];
                     } else {
                         $subpatternNameCounts[$subpatternName] = 1;
                     }
                     // Add the var as a named subpattern
                     $urlPattern .= '(?P<' . preg_quote($subpatternName) . '>' . $part[1] . ')';
                 } else {
                     // Just match it
                     $urlPattern .= '(' . $part[1] . ')';
                 }
             }
         }
     }
     $routeRecord->locale = $locale;
     $routeRecord->urlParts = JsonHelper::encode($urlParts);
     $routeRecord->urlPattern = $urlPattern;
     $routeRecord->template = $template;
     $routeRecord->save();
     return $routeRecord;
 }
 /**
  * Returns all routes.
  */
 public function getAllRoutes()
 {
     $return = array();
     $routes = RouteRecord::model()->ordered()->findAll();
     foreach ($routes as $route) {
         $urlDisplayHtml = '';
         $urlParts = JsonHelper::decode($route->urlParts);
         foreach ($urlParts as $part) {
             if (is_string($part)) {
                 $urlDisplayHtml .= $part;
             } else {
                 $urlDisplayHtml .= '<span class="token" data-name="' . $part[0] . '" data-value="' . $part[1] . '">' . $part[0] . '</span>';
             }
         }
         $return[] = array('id' => $route->id, 'urlDisplayHtml' => $urlDisplayHtml, 'template' => $route->template);
     }
     return $return;
 }
 /**
  * Returns a route by its ID.
  *
  * @param int $routeId The route ID
  * @return RouteRecord|null
  */
 private function _getRecordRouteById($routeId)
 {
     return RouteRecord::model()->findById($routeId);
 }