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).
issue