Repository: wp-graphql/wp-graphql (PHP)

Issue:
On ContentType union in nodeByUri returns incorrect isPostsPage
### Description

`...on ContentType` union in `nodeByUri` returns incorrect `isPostsPage`

### Steps to reproduce

1. Set your WordPress instance to have a static posts page
2. Use the following query to query it by uri (in my case `/blog`):
    ```gql
    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        ... on ContentType {
          isPostsPage
        }
      }
    }
    ```
3. Notice `isPostsPage` returns `false`

### Screenshots

<img width="2560" alt="Screen Shot 2022-09-09 at 2 50 30 PM" src="https://user-images.githubusercontent.com/5946219/189432530-a259bf9c-45ad-475a-9069-2960758e1b3e.png">

<img width="2560" alt="Screen Shot 2022-09-09 at 2 50 42 PM" src="https://user-images.githubusercontent.com/5946219/189432538-72d92645-da38-4eaf-b331-0071b2e3ab08.png">



### Additional context

_No response_

### WPGraphQL Version

1.10.0

### WordPress Version

6.0.2

### PHP Version

7.4.1

### Additional enviornment details

_No response_

### Please confirm that you have searched existing issues in the repo.

- [X] Yes

### Please confirm that you have disabled ALL plugins except for WPGraphQL.

- [X] Yes
- [ ] My issue is with compatibility with a specific WordPress plugin, and I have listed all my installed plugins (and version info) above.


Discussion: Related: #2486 (can probably be temporarily resolved before #2491)
cc: #2513 
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

confirmed not stale wp 6.4.1 + wpgraphql 1.18.2
![image](https://github.com/wp-graphql/wp-graphql/assets/29322304/10d6bb11-bef3-4bb1-bb69-0c3645e15e49)

Ok, so this might be more of a "what's the right behavior" question. 

With the following settings:

![CleanShot 2024-02-16 at 11 07 18](https://github.com/wp-graphql/wp-graphql/assets/1260765/5ec53e82-b6ad-49d2-8e96-6168807df2ef)

WordPress itself doesn't actually return an archive of a content type _or_ a page 🤔 

We can test this with the following snippet: 

```php
add_action( 'shutdown', function() {
	global $wp_query;
	var_dump( [
		'is_home' => is_home(),
		'is_page' => is_page(),
		'is_singular' => is_singular(),
		'is_archive' => is_archive(),
		'is_posts_page' => $wp_query->is_posts_page,
	]);
});
```

Then (with the Twenty Twenty-Two theme active) visit the `/blog` url.

Scroll to the bottom of the page to see output of the var dump. 

![CleanShot 2024-02-16 at 11 11 47](https://github.com/wp-graphql/wp-graphql/assets/1260765/11b35874-6c7c-41a3-8038-88d5e8f2f732)

WordPress doesn't treat this page as a "Page" _or_ an "Archive" / "ContentType". 

It's in some limbo state between a page and the post archive 🤔 

If we add `'wp_query' => $wp_query` to our snippet and inspect again: 

We will see that WordPress still doesn't treat this as a page _or_ an archive, BUT we will see that the `$wp_query->queried_object` is a Page. 

So, the URI is resolving to a page, but then WordPress has logic that determines instead of returning a page or an archive it should return a "blog" template instead. 🤔 

WPGraphQL doesn't have the concept of a `HomePage` object type in the Schema, so it needs to chose to return another type of valid node. Should it return the Page? or should it return the ContentType representing the "posts" archive?

Currently, WPGraphQL resolves the `ContentType`, and does indeed return `isPostsPage: false`

I think based on our prior decision to return a `ContentType` instead of a page, I'm inclined to consider this a valid bug and that we _should_ return `isPostsPage: true` for the `ContentType` object being returned for the uri that matches the "posts page". 

I'll work up a PR to resolve this. 
^ Regarding the philosophical decision to return a ContentType or a Page, we went the other way with the Front Page. 

If you set the `page_on_front` to be a Page, then query the uri `/` we get a `Page` returned instead of a ContentType.

So it seems like we've got conflicting logic here. 

It seems like if the user sets the homepage to be a page and the posts page to be a page, they should, well, be a page? 🤔 
Given these settings, I think I would expect both of these things to be pages 🤔 

![CleanShot 2024-02-16 at 11 50 47](https://github.com/wp-graphql/wp-graphql/assets/1260765/3e78f5cc-55ca-4fa7-b5d4-d9487f6ca9db)

But we get a `Page` for the Home url and a `ContentType` for the `/blog` url 🤔 

![CleanShot 2024-02-16 at 11 53 21](https://github.com/wp-graphql/wp-graphql/assets/1260765/94fe5a9e-a5f1-49af-a509-8797e8ce752e)

Considering this will get an 'ideal resolution' via #2491, I think the reason to warrant a change in behavior should be around _use case_ and less about philosophy.

Since it _currently_ resolves a `ContentType` we need a programmatic way to know there's `Page` data that needs to be associated with the node (e.g. SEO, template deets), which isn't a concern with the home page.

So if we _do_ decide to address this before the `Archive` interface, I think treating this as a bug is the way to go.
I guess I'm still torn on what the _ideal_ should be. 

I think the best short-term solution is to still return true if/when a `ContentType` is being returned. 

But I think we're limiting users here (and maybe we consider this when we address #2491) because in monolithic WordPress, you would be able to build a page template and use data from the queried object (the page) and do things like output meta stored on that page within the template. 

But right now, we're outputting a `ContentType` and it has no relation to the "Page" that is actually set to this value, so users can't actually even get fields, etc from the Page that is set as the "blog" page. 🤔 

I'll proceed with the PR to resolve the `isPostsPage` still, but ultimately it's still not sitting right with me 😬 
> But right now, we're outputting a ContentType and it has no relation to the "Page" that is actually set to this value, so users can't actually even get fields, etc from the Page that is set as the "blog" page.

Agreed 💯

An ideal **breaking** pattern imo is that uris resolve to an ArchiveNode (that wraps the ContentNode and ContentType together) instead of the ContentType altogether.

The proposed non-breaking `ContentTypeArchive` interface would just provide the connection to the `Page`, meaning `isPostsPage === true` followed by a second second query to the page ID could be replaced with checking if the `contentTypeNode?.connectedPage` was included in the returned results.

And once the new non-bc Node _type_ is implemented, we can opt for philosophy and revert this since a `ContentType will _never_ be a posts page.
![CleanShot 2024-02-16 at 12 21 50](https://github.com/wp-graphql/wp-graphql/assets/1260765/85d0330f-972f-47b2-86d4-cd0e333978b3)

If I fix this bug to return `isPostsPage: true` on the `ContentType` for the "post" post type, then I think I need to update the `Page.isPostsPage` to return false. 

The idea behind `isPostsPage: true` is to identify the node that represents the posts page, and if our `nodeByUri( uri: /blog' )` has determined that the posts page is represented by a `ContentType` then `page.isPostsPage: true` should be considered incorrect as there can only be 1 node representing the posts page.
This is where I think the conflict is right now: 

- https://github.com/wp-graphql/wp-graphql/blob/develop/src/Data/NodeResolver.php#L641
- https://github.com/wp-graphql/wp-graphql/blob/develop/src/Data/NodeResolver.php#L226

For one condition we've decided that if `show_on_front` is set to `page` we will return a `Page` but in the other case we've decided that we should not return a `Page` 🤔 
> The idea behind isPostsPage: true is to identify the node that represents the posts page, and if our nodeByUri( uri: /blog' ) has determined that the posts page is represented by a ContentType then page.isPostsPage: true should be considered incorrect as there can only be 1 node representing the posts page.

Philosophically, yes. Practically however, the "page" in this context doesn't refer to to the "one true `Page` node", but more colloquially to "this relates to the data used to hydrate the view for the Posts archive.

I understand the argument of not wanting to break schema purity because the archive interface will obviate the problem, but if you do decide you want to implement this first, I don't think it's a concern that both the `Page`  and `ContentType` have `isPostsPage` true.
This (and the followups) is probably relevant https://github.com/wp-graphql/wp-graphql/issues/2486#issuecomment-1232005508
Ok. I'll leave the `Page.isPostsPage` field as-is for now. 

also related: 

- https://github.com/wp-graphql/wp-graphql/issues/1687
- https://github.com/wp-graphql/wp-graphql/pull/2464
@blakewilson see: #3048



Fix (patch):
diff --git a/src/Model/PostType.php b/src/Model/PostType.php
index 251fecdca..9e054cbee 100644
--- a/src/Model/PostType.php
+++ b/src/Model/PostType.php
@@ -182,16 +182,8 @@ protected function init() {
 				},
 				// If the homepage settings are to set to
 				'isPostsPage'         => function () {
-					if (
-						'post' === $this->name &&
-						(
-							'posts' === get_option( 'show_on_front', 'posts' ) ||
-							empty( (int) get_option( 'page_for_posts', 0 ) ) )
-					) {
-						return true;
-					}
-
-					return false;
+					// the "post" ContentType is always represented as isPostsPage
+					return 'post' === $this->name;
 				},
 				'isFrontPage'         => function () {
 					if (
diff --git a/src/Type/InterfaceType/UniformResourceIdentifiable.php b/src/Type/InterfaceType/UniformResourceIdentifiable.php
index 9918c7ce2..e0e24238a 100644
--- a/src/Type/InterfaceType/UniformResourceIdentifiable.php
+++ b/src/Type/InterfaceType/UniformResourceIdentifiable.php
@@ -46,6 +46,20 @@ public static function register_type( TypeRegistry $type_registry ) {
 							return $node instanceof Term;
 						},
 					],
+					'isFrontPage'   => [
+						'type'        => [ 'non_null' => 'Bool' ],
+						'description' => __( 'Whether the node represents the front page.', 'wp-graphql' ),
+						'resolve'     => static function ( $node, $args, $context, $info ) {
+							return isset( $node->isFrontPage ) && (bool) $node->isFrontPage;
+						},
+					],
+					'isPostsPage'   => [
+						'type'        => [ 'non_null' => 'Bool' ],
+						'description' => __( 'Whether  the node represents the blog page.', 'wp-graphql' ),
+						'resolve'     => static function ( $node, $args, $context, $info ) {
+							return isset( $node->isPostsPage ) && (bool) $node->isPostsPage;
+						},
+					],
 					'isComment'     => [
 						'type'        => [ 'non_null' => 'Boolean' ],
 						'description' => __( 'Whether the node is a Comment', 'wp-graphql' ),