Skip to content

Not all mandatory fields in episode creation are marked so?

Describe the bug

Failing to fill new episode title in creation form incorrectly does not show form validation error, instead showing HTTP 50x.

Steps to reproduce

  1. Go to admin->some podcast->new episode
  2. Only select audio file (omit title!)

Expected behavior

Form validation fails, telling you to fill the title.

Actual behavior

50x in routing (?).

Relevant logs and/or screenshots

Dump from CI_ENVIRONMENT="development" run:

CodeIgniter\Router\Exceptions\RouterException

Router.invalidParameterType

SYSTEMPATH/Router/RouteCollection.php at line 1378

1371 
1372             // Remove `(:` and `)` when $placeholder is a placeholder.
1373             $placeholderName = substr($placeholder, 2, -1);
1374             // or maybe $placeholder is not a placeholder, but a regex.
1375             $pattern = $this->placeholders[$placeholderName] ?? $placeholder;
1376 
1377             if (! preg_match('#^' . $pattern . '$#u', $params[$index])) {
1378                 throw RouterException::forInvalidParameterType();
1379             }
1380 
1381             // Ensure that the param we're inserting matches
1382             // the expected param type.
1383             $pos  = strpos($from, $placeholder);
1384             $from = substr_replace($from, $params[$index], $pos, strlen($placeholder));
1385         }

    Backtrace Server Request Response Files Memory 

    SYSTEMPATH/Router/RouteCollection.php : 1378   —  CodeIgniter\Router\Exceptions\RouterException::forInvalidParameterType ()

    1371 
    1372             // Remove `(:` and `)` when $placeholder is a placeholder.
    1373             $placeholderName = substr($placeholder, 2, -1);
    1374             // or maybe $placeholder is not a placeholder, but a regex.
    1375             $pattern = $this->placeholders[$placeholderName] ?? $placeholder;
    1376 
    1377             if (! preg_match('#^' . $pattern . '$#u', $params[$index])) {
    1378                 throw RouterException::forInvalidParameterType();
    1379             }
    1380 
    1381             // Ensure that the param we're inserting matches
    1382             // the expected param type.
    1383             $pos  = strpos($from, $placeholder);
    1384             $from = substr_replace($from, $params[$index], $pos, strlen($placeholder));
    1385         }

    SYSTEMPATH/Router/RouteCollection.php : 1169   —  CodeIgniter\Router\RouteCollection->buildReverseRoute ()

    1162         // Named routes get higher priority.
    1163         foreach ($this->routesNames as $verb => $collection) {
    1164             if (array_key_exists($search, $collection)) {
    1165                 $routeKey = $collection[$search];
    1166 
    1167                 $from = $this->routes[$verb][$routeKey]['from'];
    1168 
    1169                 return $this->buildReverseRoute($from, $params);
    1170             }
    1171         }
    1172 
    1173         // Add the default namespace if needed.
    1174         $namespace = trim($this->defaultNamespace, '\\') . '\\';
    1175         if (
    1176             substr($search, 0, 1) !== '\\'

    SYSTEMPATH/Common.php : 978   —  CodeIgniter\Router\RouteCollection->reverseRoute ()

    971      * @param int|string ...$params One or more parameters to be passed to the route.
    972      *                              The last parameter allows you to set the locale.
    973      *
    974      * @return false|string The route (URI path relative to baseURL) or false if not found.
    975      */
    976     function route_to(string $method, ...$params)
    977     {
    978         return Services::routes()->reverseRoute($method, ...$params);
    979     }
    980 }
    981 
    982 if (! function_exists('session')) {
    983     /**
    984      * A convenience method for accessing the session instance,
    985      * or an item that has been set in the session.

    SYSTEMPATH/Helpers/url_helper.php : 482   —   route_to()

    APPPATH/Entities/Episode.php : 454   —   url_to()

    APPPATH/Entities/Episode.php : 466   —  App\Entities\Episode->getLink ()

    459         return $theme
    460                 ? url_to('embed-theme', esc($this->getPodcast()->handle), esc($this->attributes['slug']), $theme)
    461                 : url_to('embed', esc($this->getPodcast()->handle), esc($this->attributes['slug']));
    462     }
    463 
    464     public function setGuid(?string $guid = null): static
    465     {
    466         $this->attributes['guid'] = $guid === null ? $this->getLink() : $guid;
    467 
    468         return $this;
    469     }
    470 
    471     public function getPodcast(): ?Podcast
    472     {
    473         return (new PodcastModel())->getPodcastById($this->podcast_id);

    SYSTEMPATH/Entity/Entity.php : 485   —  App\Entities\Episode->setGuid ()

    478             $this->{'_' . $method}($value);
    479 
    480             return;
    481         }
    482 
    483         // If a "`set` + $key" method exists, it is also a setter.
    484         if (method_exists($this, $method) && $method !== 'setAttributes') {
    485             $this->{$method}($value);
    486 
    487             return;
    488         }
    489 
    490         // Otherwise, just the value. This allows for creation of new
    491         // class properties that are undefined, though they cannot be
    492         // saved. Useful for grabbing values through joins, assigning

    SYSTEMPATH/Entity/Entity.php : 152   —  CodeIgniter\Entity\Entity->__set ()

    145     public function fill(?array $data = null)
    146     {
    147         if (! is_array($data)) {
    148             return $this;
    149         }
    150 
    151         foreach ($data as $key => $value) {
    152             $this->__set($key, $value);
    153         }
    154 
    155         return $this;
    156     }
    157 
    158     /**
    159      * General method that will return all public and protected values

    SYSTEMPATH/Entity/Entity.php : 133   —  CodeIgniter\Entity\Entity->fill ()

    126     /**
    127      * Allows filling in Entity parameters during construction.
    128      */
    129     public function __construct(?array $data = null)
    130     {
    131         $this->syncOriginal();
    132 
    133         $this->fill($data);
    134     }
    135 
    136     /**
    137      * Takes an array of key/value pairs and sets them as class
    138      * properties, using any `setCamelCasedProperty()` methods
    139      * that may or may not exist.
    140      *

    ROOTPATH/modules/Admin/Controllers/EpisodeController.php : 195   —  CodeIgniter\Entity\Entity->__construct ()

    188                 ->withInput()
    189                 ->with('error', lang('Episode.messages.sameSlugError'));
    190         }
    191 
    192         $db = db_connect();
    193         $db->transStart();
    194 
    195         $newEpisode = new Episode([
    196             'created_by'           => user_id(),
    197             'updated_by'           => user_id(),
    198             'podcast_id'           => $this->podcast->id,
    199             'title'                => $this->request->getPost('title'),
    200             'slug'                 => $this->request->getPost('slug'),
    201             'guid'                 => null,
    202             'audio'                => $this->request->getFile('audio_file'),

    ROOTPATH/modules/Admin/Controllers/EpisodeController.php : 64   —  Modules\Admin\Controllers\EpisodeController->attemptCreate ()

    57 
    58             $this->episode = $episode;
    59 
    60             unset($params[1]);
    61             unset($params[0]);
    62         }
    63 
    64         return $this->{$method}(...$params);
    65     }
    66 
    67     public function list(): string
    68     {
    69         /** @var ?string $query */
    70         $query = $this->request->getGet('q');
    71 

    SYSTEMPATH/CodeIgniter.php : 941   —  Modules\Admin\Controllers\EpisodeController->_remap ()

    934      */
    935     protected function runController($class)
    936     {
    937         // This is a Web request or PHP CLI request
    938         $params = $this->router->params();
    939 
    940         $output = method_exists($class, '_remap')
    941             ? $class->_remap($this->method, ...$params)
    942             : $class->{$this->method}(...$params);
    943 
    944         $this->benchmark->stop('controller');
    945 
    946         return $output;
    947     }
    948 

    SYSTEMPATH/CodeIgniter.php : 502   —  CodeIgniter\CodeIgniter->runController ()

    495             if (! method_exists($controller, '_remap') && ! is_callable([$controller, $this->method], false)) {
    496                 throw PageNotFoundException::forMethodNotFound($this->method);
    497             }
    498 
    499             // Is there a "post_controller_constructor" event?
    500             Events::trigger('post_controller_constructor');
    501 
    502             $returned = $this->runController($controller);
    503         } else {
    504             $this->benchmark->stop('controller_constructor');
    505             $this->benchmark->stop('controller');
    506         }
    507 
    508         // If $returned is a string, then the controller output something,
    509         // probably a view, instead of echoing it directly. Send it along

    SYSTEMPATH/CodeIgniter.php : 361   —  CodeIgniter\CodeIgniter->handleRequest ()

    354 
    355         $this->getRequestObject();
    356         $this->getResponseObject();
    357 
    358         $this->spoofRequestMethod();
    359 
    360         try {
    361             $this->response = $this->handleRequest($routes, config(Cache::class), $returnResponse);
    362         } catch (ResponsableInterface|DeprecatedRedirectException $e) {
    363             $this->outputBufferingEnd();
    364             if ($e instanceof DeprecatedRedirectException) {
    365                 $e = new RedirectException($e->getMessage(), $e->getCode(), $e);
    366             }
    367 
    368             $this->response = $e->getResponse();

    FCPATH/index.php : 85   —  CodeIgniter\CodeIgniter->run ()

    78  *---------------------------------------------------------------
    79  * LAUNCH THE APPLICATION
    80  *---------------------------------------------------------------
    81  * Now that everything is set up, it's time to actually fire
    82  * up the engines and make this app do its thang.
    83  */
    84 
    85 $app->run();
    86 
    87 // Save Config Cache
    88 // $factoriesCache->save('config');
    89 // ^^^ Uncomment this line if you want to use Config Caching.
    90 
    91 // Exits the application, setting the exit code for CLI-based applications
    92 // that might be watching.

Displayed at 19:17:29pm — PHP: 8.2.14 — CodeIgniter: 4.4.3 -- Environment: development

Context

  • Castopod: 1.7.3
  • OS: Debian 11
  • Browser: Firefox 121
  • Web server: NGINX
  • Yunohost 11.2.8 with Castopod 1.7.3~ynh2

Possible fixes

Works when I fill title field (i.e. missing description properly causes validation error).

Edited by orhtej2