input = $input ?? new Input(); } /** * Create (Request::get()['page'] is the create screen) or update (the edit screen) the page. * * @return array{id: int, created: bool} * * @throws PageInvalid When the payload does not pass the rules. */ public function save(Validation $validate): array { // The rules below compare with the pages as they are stored now, so read them before anything is written. Data::getAllFromPageValueColumn(); $errors = Validator::collect($validate); if ($errors) { throw new PageInvalid($errors); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- a list of messages for the REST response, not output } $fields = isset(Request::post()['existing_menu']) ? $this->input->exitingMenuFields($validate) : $this->input->customMenuFields($validate); $this->deleteOptionsOfRemovedFields(); if (Request::get()['page'] === SubpageCreate::SLUG) { return $this->create($fields); } return $this->update($fields); } /** * @return array{id: int, created: bool} */ private function create(array $fields): array { $id = PageRepository::insert($fields['menu_slug'], $fields['stored_value']); OptionNames::sync(); OptionNames::createFor(PageRepository::decode($fields['stored_value'])); Flash::add(['pages_saved' => true]); return ['id' => $id, 'created' => true]; } /** * @return array{id: int, created: bool} */ private function update(array $fields): array { $id = (int) Data::IdEditPage(); PageRepository::update($id, $fields['menu_slug'], $fields['stored_value']); OptionNames::sync(); OptionNames::updateFor(PageRepository::decode($fields['stored_value'])); return ['id' => $id, 'created' => false]; } /** * Options of fields that were on the page before and are gone now. * * @return Collection */ private function deleteOptionsOfRemovedFields() { return Collection::make(Data::getAllRegisteredFieldNames()) ->diff(Data::getFieldsWithFieldNames()) ->values() ->map(function ($name) { return delete_option($name); }); } }