PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / abilities / application / post-seo-data-updater.php

post-seo-data-updater.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI trunk, at src/abilities/application/post-seo-data-updater.php

127 lines 3.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
4 namespace Yoast\WP\SEO\Abilities\Application;
5
6 use WP_Error;
7 use Yoast\WP\SEO\Abilities\Infrastructure\Post_Access_Checker;
8 use Yoast\WP\SEO\Abilities\Infrastructure\Post_Identifier_Resolver;
9 use Yoast\WP\SEO\Abilities\Infrastructure\Post_SEO_Field_Map;
10 use Yoast\WP\SEO\Builders\Indexable_Builder;
11 use Yoast\WP\SEO\Helpers\Indexable_To_Postmeta_Helper;
12
13 /**
14 * Application service that updates the SEO data of a single post.
15 *
16 * The input is applied onto the resolved indexable (the desired state), cascaded
17 * to post meta (the source of truth) via Indexable_To_Postmeta_Helper, and the
18 * indexable is then rebuilt from that meta so reads and the rest of the plugin
19 * stay consistent.
20 */
21 class Post_SEO_Data_Updater {
22
23 /**
24 * The post identifier resolver.
25 *
26 * @var Post_Identifier_Resolver
27 */
28 private $resolver;
29
30 /**
31 * The post SEO field map.
32 *
33 * @var Post_SEO_Field_Map
34 */
35 private $field_map;
36
37 /**
38 * The post access checker.
39 *
40 * @var Post_Access_Checker
41 */
42 private $post_access_checker;
43
44 /**
45 * The indexable-to-postmeta helper.
46 *
47 * @var Indexable_To_Postmeta_Helper
48 */
49 private $indexable_to_postmeta;
50
51 /**
52 * The indexable builder.
53 *
54 * @var Indexable_Builder
55 */
56 private $indexable_builder;
57
58 /**
59 * Constructor.
60 *
61 * @param Post_Identifier_Resolver $resolver The post identifier resolver.
62 * @param Post_SEO_Field_Map $field_map The post SEO field map.
63 * @param Post_Access_Checker $post_access_checker The post access checker.
64 * @param Indexable_To_Postmeta_Helper $indexable_to_postmeta The indexable-to-postmeta helper.
65 * @param Indexable_Builder $indexable_builder The indexable builder.
66 */
67 public function __construct(
68 Post_Identifier_Resolver $resolver,
69 Post_SEO_Field_Map $field_map,
70 Post_Access_Checker $post_access_checker,
71 Indexable_To_Postmeta_Helper $indexable_to_postmeta,
72 Indexable_Builder $indexable_builder
73 ) {
74 $this->resolver = $resolver;
75 $this->field_map = $field_map;
76 $this->post_access_checker = $post_access_checker;
77 $this->indexable_to_postmeta = $indexable_to_postmeta;
78 $this->indexable_builder = $indexable_builder;
79 }
80
81 /**
82 * Updates the SEO data of the post matching the input and returns its new state.
83 *
84 * Only fields present in the input are changed (patch semantics); a present but
85 * empty value clears the field.
86 *
87 * @param array<string, int|string|bool|null> $input The input identifying the post plus the SEO fields to change.
88 *
89 * @return array<string, int|string|bool|null>|WP_Error The updated SEO data, or an error.
90 */
91 public function update_post_seo_data( array $input ) {
92 $indexable = $this->resolver->resolve_one( $input );
93
94 if ( $indexable instanceof WP_Error ) {
95 return $indexable;
96 }
97
98 // The capability gate on the ability is site-wide; the write itself is
99 // only allowed on posts the user could also edit through the regular editors.
100 $can_edit = $this->post_access_checker->ensure_can_edit( (int) $indexable->object_id );
101
102 if ( $can_edit instanceof WP_Error ) {
103 return $can_edit;
104 }
105
106 $changed_columns = $this->field_map->apply_to_indexable( $input, $indexable );
107
108 foreach ( $changed_columns as $column ) {
109 $this->indexable_to_postmeta->map_column_to_postmeta( $indexable, $column, true );
110 }
111
112 // A post-meta write does not trigger the indexable watcher, so rebuild the read model
113 // explicitly from the meta we just wrote.
114 $rebuilt = $this->indexable_builder->build_for_id_and_type( (int) $indexable->object_id, 'post', $indexable );
115
116 if ( ! $rebuilt ) {
117 return new WP_Error(
118 'yoast_seo_post_data_unavailable',
119 \__( 'The post SEO data was saved but the indexable could not be rebuilt.', 'wordpress-seo' ),
120 [ 'status' => 500 ],
121 );
122 }
123
124 return $this->field_map->to_seo_array( $rebuilt );
125 }
126 }
127