original_string: public function sort($key, $title = null, array $options = [])
      {
          $options += ['url' => [], 'model' => null, 'escape' => true];
          $url = $options['url'];
          unset($options['url']);
  
          if (empty($title)) {
              $title = $key;
  
              if (strpos($title, '.') !== false) {
                  $title = str_replace('.', ' ', $title);
              }
  
              $title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)));
          }
  
          $defaultDir = isset($options['direction']) ? strtolower($options['direction']) : 'asc';
          unset($options['direction']);
  
          $locked = isset($options['lock']) ? $options['lock'] : false;
          unset($options['lock']);
  
          $sortKey = $this->sortKey($options['model']);
          $defaultModel = $this->defaultModel();
          $model = $options['model'] ?: $defaultModel;
          list($table, $field) = explode('.', $key . '.');
          if (!$field) {
              $field = $table;
              $table = $model;
          }
          $isSorted = (
              $sortKey === $table . '.' . $field ||
              $sortKey === $model . '.' . $key ||
              $table . '.' . $field === $model . '.' . $sortKey
          );
  
          $template = 'sort';
          $dir = $defaultDir;
          if ($isSorted) {
              if ($locked) {
                  $template = $dir === 'asc' ? 'sortDescLocked' : 'sortAscLocked';
              } else {
                  $dir = $this->sortDir($options['model']) === 'asc' ? 'desc' : 'asc';
                  $template = $dir === 'asc' ? 'sortDesc' : 'sortAsc';
              }
          }
          if (is_array($title) && array_key_exists($dir, $title)) {
              $title = $title[$dir];
          }
  
          $url = array_merge(
              ['sort' => $key, 'direction' => $dir, 'page' => 1],
              $url,
              ['order' => null]
          );
          $vars = [
              'text' => $options['escape'] ? h($title) : $title,
              'url' => $this->generateUrl($url, $options['model']),
          ];
  
          return $this->templater()->format($template, $vars);
      }
code: public function sort($key, $title = null, array $options = [])
      {
          $options += ['url' => [], 'model' => null, 'escape' => true];
          $url = $options['url'];
          unset($options['url']);
  
          if (empty($title)) {
              $title = $key;
  
              if (strpos($title, '.') !== false) {
                  $title = str_replace('.', ' ', $title);
              }
  
              $title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)));
          }
  
          $defaultDir = isset($options['direction']) ? strtolower($options['direction']) : 'asc';
          unset($options['direction']);
  
          $locked = isset($options['lock']) ? $options['lock'] : false;
          unset($options['lock']);
  
          $sortKey = $this->sortKey($options['model']);
          $defaultModel = $this->defaultModel();
          $model = $options['model'] ?: $defaultModel;
          list($table, $field) = explode('.', $key . '.');
          if (!$field) {
              $field = $table;
              $table = $model;
          }
          $isSorted = (
              $sortKey === $table . '.' . $field ||
              $sortKey === $model . '.' . $key ||
              $table . '.' . $field === $model . '.' . $sortKey
          );
  
          $template = 'sort';
          $dir = $defaultDir;
          if ($isSorted) {
              if ($locked) {
                  $template = $dir === 'asc' ? 'sortDescLocked' : 'sortAscLocked';
              } else {
                  $dir = $this->sortDir($options['model']) === 'asc' ? 'desc' : 'asc';
                  $template = $dir === 'asc' ? 'sortDesc' : 'sortAsc';
              }
          }
          if (is_array($title) && array_key_exists($dir, $title)) {
              $title = $title[$dir];
          }
  
          $url = array_merge(
              ['sort' => $key, 'direction' => $dir, 'page' => 1],
              $url,
              ['order' => null]
          );
          $vars = [
              'text' => $options['escape'] ? h($title) : $title,
              'url' => $this->generateUrl($url, $options['model']),
          ];
  
          return $this->templater()->format($template, $vars);
      }
code_tokens: public function sort ( $ key , $ title = null , array $ options = [ ] ) { $ options += [ 'url' => [ ] , 'model' => null , 'escape' => true ] ; $ url = $ options [ 'url' ] ; unset ( $ options [ 'url' ] ) ; if ( empty ( $ title ) ) { $ title = $ key ; if ( strpos ( $ title , '.' ) !== false ) { $ title = str_replace ( '.' , ' ' , $ title ) ; } $ title = __ ( Inflector :: humanize ( preg_replace ( '/_id$/' , '' , $ title ) ) ) ; } $ defaultDir = isset ( $ options [ 'direction' ] ) ? strtolower ( $ options [ 'direction' ] ) : 'asc' ; unset ( $ options [ 'direction' ] ) ; $ locked = isset ( $ options [ 'lock' ] ) ? $ options [ 'lock' ] : false ; unset ( $ options [ 'lock' ] ) ; $ sortKey = $ this -> sortKey ( $ options [ 'model' ] ) ; $ defaultModel = $ this -> defaultModel ( ) ; $ model = $ options [ 'model' ] ? : $ defaultModel ; list ( $ table , $ field ) = explode ( '.' , $ key . '.' ) ; if ( ! $ field ) { $ field = $ table ; $ table = $ model ; } $ isSorted = ( $ sortKey === $ table . '.' . $ field || $ sortKey === $ model . '.' . $ key || $ table . '.' . $ field === $ model . '.' . $ sortKey ) ; $ template = 'sort' ; $ dir = $ defaultDir ; if ( $ isSorted ) { if ( $ locked ) { $ template = $ dir === 'asc' ? 'sortDescLocked' : 'sortAscLocked' ; } else { $ dir = $ this -> sortDir ( $ options [ 'model' ] ) === 'asc' ? 'desc' : 'asc' ; $ template = $ dir === 'asc' ? 'sortDesc' : 'sortAsc' ; } } if ( is_array ( $ title ) && array_key_exists ( $ dir , $ title ) ) { $ title = $ title [ $ dir ] ; } $ url = array_merge ( [ 'sort' => $ key , 'direction' => $ dir , 'page' => 1 ] , $ url , [ 'order' => null ] ) ; $ vars = [ 'text' => $ options [ 'escape' ] ? h ( $ title ) : $ title , 'url' => $ this -> generateUrl ( $ url , $ options [ 'model' ] ) , ] ; return $ this -> templater ( ) -> format ( $ template , $ vars ) ; }
docstring: Generates a sorting link. Sets named parameters for the sort and direction. Handles
  direction switching automatically.
  
  ### Options:
  
  - `escape` Whether you want the contents html entity encoded, defaults to true.
  - `model` The model to use, defaults to PaginatorHelper::defaultModel().
  - `direction` The default direction to use when this link isn't active.
  - `lock` Lock direction. Will only use the default direction then, defaults to false.
  
  @param string $key The name of the key that the recordset should be sorted.
  @param string|null $title Title for the link. If $title is null $key will be used
  for the title and will be generated by inflection.
  @param array $options Options for sorting link. See above for list of keys.
  @return string A link sorting default by 'asc'. If the resultset is sorted 'asc' by the specified
  key the returned link will sort by 'desc'.
  @link https://book.cakephp.org/3.0/en/views/helpers/paginator.html#creating-sort-links
docstring_tokens: Generates a sorting link . Sets named parameters for the sort and direction . Handles direction switching automatically .