Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ Add the template name as a comment at the top of this template file like this:

Now, you will be able to select the template from the editor. The name of the template is displayed using the value in your docblock.

### ACF Post Object Field

It is possible to add a connection filter to the block settings that lets you filter the available posts based on connected posts. This only works for the ACF "Post Object" field.

**Important: For this to work, the Post Object field setting "Select Multiple" must be disabled.**

All you need to do is publish `config/yard-query-block.php` and register all connections in the `connections` array. For example:

```php
return [
'connections' => [
[
'from' => 'news',
'to' => 'project',
'meta_key' => 'news_related_project',
],
],
];
```

## Hooks

### JavaScript filters
Expand Down
20 changes: 14 additions & 6 deletions assets/components/filters-controls/connection-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,18 @@ const ConnectionControl = ( props ) => {
);
} );

if ( connectionPostTypes.length < 1 ) {
// Filter connections to only include unique post types (avoid duplicates if multiple connections to the same post type exist)
const uniqueConnectionPostTypes = connectionPostTypes.filter(
( value, index, self ) =>
index === self.findIndex( ( t ) => t.value === value.value )
);
Comment thread
SimonvanWijhe marked this conversation as resolved.

if ( uniqueConnectionPostTypes.length < 1 ) {
setConnections( [] );
return;
}

setConnections( connectionPostTypes );
setConnections( uniqueConnectionPostTypes );
};

getConnections();
Expand All @@ -92,10 +98,12 @@ const ConnectionControl = ( props ) => {
const newConnectionPosts = {};

connections.forEach( ( connection ) => {
newConnectionPosts[ connection.value ] = {
value: currentPostId,
label: currentPostTitle,
};
newConnectionPosts[ connection.value ] = [
{
value: currentPostId,
label: currentPostTitle,
},
];
} );

setAttributes( {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const ConnectionSelectControl = ( props ) => {
enable={ true }
handleChange={ onChange }
isClearable={ true }
isMulti={ false }
isMulti={ true }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deze moet nu toch weer op false? Want dat werkt toch niet?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ja dit werkt dus wel :)

Wat niet werkt is wanneer "Select Multiple" is aangevinkt:

Post-Object-General-Settings

Dan slaat acf alle post ID's als een serialized array op in de database.

label={ connection.label }
value={ connectionPosts[ connection.value ] }
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const ManualSelectionToggleControl = ( props ) => {
postParent: {},
enableTaxonomies: false,
taxonomyTerms: undefined,
enableConnections: false,
enableConnection: false,
connectionPosts: {},
} );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ const PostTypeSelectControl = ( props ) => {
postParent: {},
enableTaxonomies: false,
taxonomyTerms: undefined,
enableConnections: false,
enableConnection: false,
connectionPosts: {},
connectionOption: 'current-post-connections',
} );
};

Expand Down
10 changes: 7 additions & 3 deletions assets/utils/helpers.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { decodeEntities } from '@wordpress/html-entities';

/**
* Map posts array to an array to use for options in a select control
*
Expand All @@ -6,7 +8,9 @@
export const mapPostsToOptions = ( options = [] ) => {
return options.map( ( item ) => ( {
value: item.id,
label: item.title ? item.title : `#${ item.id }: geen titel`,
label: item.title
? decodeEntities( item.title )
: `#${ item.id }: geen titel`,
} ) );
};

Expand All @@ -17,7 +21,7 @@ export const mapPostsToOptions = ( options = [] ) => {
*/
export const mapPostTypesToOptions = ( postTypes = [] ) => {
return postTypes.map( ( item ) => ( {
label: item.name.replace( '&#39;', "'" ),
label: decodeEntities( item.name ),
value: item.slug,
} ) );
};
Expand All @@ -29,7 +33,7 @@ export const mapPostTypesToOptions = ( postTypes = [] ) => {
*/
export const mapTermsToOptions = ( terms = [] ) => {
return terms.map( ( term ) => ( {
label: term.name.replace( '&#39;', "'" ),
label: decodeEntities( term.name ),
value: term.slug,
} ) );
};
Expand Down
2 changes: 1 addition & 1 deletion config/yard-query-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
'connections' => [
// [
// 'from' => 'news',
// 'meta_key' => 'news_connected_project',
// 'to' => 'project',
// 'meta_key' => 'news_related_project',
// ],
],
];
2 changes: 0 additions & 2 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ parameters:
paths:
- src
tmpDir: build/phpstan
ignoreErrors:
- message: '#Call to an undefined method Corcel\\Model\\Builder\\PostBuilder::hasMeta\(\)#'
2 changes: 1 addition & 1 deletion public/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-server-side-render'), 'version' => '646477fe0cd8e94f969b');
<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-server-side-render'), 'version' => '117be44835fd74eb02e7');
2 changes: 1 addition & 1 deletion public/index.js

Large diffs are not rendered by default.

48 changes: 33 additions & 15 deletions src/Block/BlockAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Yard\QueryBlock\Block;

use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Spatie\LaravelData\Attributes\WithCast;
use Spatie\LaravelData\Data;
Expand All @@ -24,8 +25,8 @@ class BlockAttributes extends Data
* @param LabelValueArray|array{} $stickyPost
* @param LabelValueArray|array{} $excludePosts
* @param LabelValueArray|array{} $postParent
* @param array{string: list<LabelValueArray>}|array{} $taxonomyTerms
* @param array{string: list<LabelValueArray>}|array{} $connectionPosts
* @param array<string, list<LabelValueArray>>|array{} $taxonomyTerms
* @param array<string, list<array{ label: string, value: int }>>|array{} $connectionPosts
*/
Comment thread
SimonvanWijhe marked this conversation as resolved.
public function __construct(
public array $postTypes = [],
Expand Down Expand Up @@ -78,40 +79,57 @@ public function postTypes(): array

public function enableConnection(): bool
{
return $this->enableConnection;
return $this->enableConnection && ! empty($this->connectionPosts);
Comment thread
SimonvanWijhe marked this conversation as resolved.
}

/**
* @return array<string, int>}
* @return list<array{
* post_type: string,
* meta_key: string,
* meta_value: list<int>,
* }>
*/
public function connectedPost(): array
public function postConnections(): array
{
$connections = [];

foreach ($this->connectionPosts as $postType => $connection) {
$metaKey = $this->connectionMetaKey($postType);
$connectionsConfig = $this->connectionsConfig();

if (empty($metaKey)) {
foreach ($this->connectionPosts as $targetPostType => $connectionArray) {
$config = $connectionsConfig->where('to', $targetPostType);

if ($config->count() === 0) {
continue;
}

$connections[$metaKey] = (int)$connection['value'];
foreach ($config as $configItem) {
$result = [];
$result['post_type'] = $configItem['from'];
$result['meta_key'] = $configItem['meta_key'];
$result['meta_value'] = array_map('intval', array_column($connectionArray, 'value'));
Comment thread
SimonvanWijhe marked this conversation as resolved.
$connections[] = $result;
}
}

Comment thread
SimonvanWijhe marked this conversation as resolved.
return $connections;
Comment thread
SimonvanWijhe marked this conversation as resolved.
}
Comment thread
SimonvanWijhe marked this conversation as resolved.

private function connectionMetaKey(string $postType): string
/**
* @return Collection<int, array{
* from: string,
* to: string,
* meta_key: string,
* }>
*/
private function connectionsConfig(): Collection
{
$config = config('yard-query-block.connections', []);

if (! is_array($config) || empty($config)) {
return '';
if (! is_array($config)) {
return collect();
}

$metaKey = collect($config)->firstWhere('to', $postType);

return $metaKey['meta_key'];
return collect($config);
}

/**
Expand Down
14 changes: 13 additions & 1 deletion src/Query/PostQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,19 @@ public function get(): Collection
}

if ($this->attributes->enableConnection()) {
$query->hasMeta($this->attributes->connectedPost());
$query->where(function ($query) {
$connected = $this->attributes->postConnections();
foreach ($connected as $connection) {
$query->orWhere(function ($q) use ($connection) {
$q->where('post_type', $connection['post_type'])
->whereHas('meta', function ($metaQuery) use ($connection) {
$metaQuery
->where('meta_key', $connection['meta_key'])
->whereIn('meta_value', $connection['meta_value']);
});
});
}
});
}

$query = $this->order($query);
Expand Down
93 changes: 93 additions & 0 deletions tests/Block/BlockAttributesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,96 @@
$attributes = BlockAttributes::from([]);
expect($attributes->align())->toBe('');
});

it('returns true for enableConnection when enabled and connectionPosts is not empty', function () {
$attributes = BlockAttributes::from([
'enableConnection' => true,
'connectionPosts' => ['page' => [['label' => 'My Page', 'value' => '42']]],
]);
expect($attributes->enableConnection())->toBeTrue();
});

it('returns false for enableConnection when enabled but connectionPosts is empty', function () {
$attributes = BlockAttributes::from([
'enableConnection' => true,
'connectionPosts' => [],
]);
expect($attributes->enableConnection())->toBeFalse();
});

it('returns false for enableConnection when disabled even if connectionPosts is not empty', function () {
$attributes = BlockAttributes::from([
'enableConnection' => false,
'connectionPosts' => ['page' => [['label' => 'My Page', 'value' => '42']]],
]);
expect($attributes->enableConnection())->toBeFalse();
});

it('returns an empty array for postConnections when no config is set', function () {
config(['yard-query-block.connections' => []]);
$attributes = BlockAttributes::from([
'enableConnection' => true,
'connectionPosts' => ['page' => [['label' => 'My Page', 'value' => '42']]],
]);
expect($attributes->postConnections())->toBe([]);
});

it('returns an empty array for postConnections when connectionPosts target has no matching config', function () {
config(['yard-query-block.connections' => [
['from' => 'post', 'to' => 'event', 'meta_key' => 'related_event'],
]]);
$attributes = BlockAttributes::from([
'enableConnection' => true,
'connectionPosts' => ['page' => [['label' => 'My Page', 'value' => '42']]],
]);
expect($attributes->postConnections())->toBe([]);
});

it('returns multiple connections when multiple config entries share the same to post type', function () {
config(['yard-query-block.connections' => [
['from' => 'post', 'to' => 'page', 'meta_key' => 'related_page'],
['from' => 'product', 'to' => 'page', 'meta_key' => 'featured_page'],
]]);
$attributes = BlockAttributes::from([
'enableConnection' => true,
'connectionPosts' => ['page' => [['label' => 'My Page', 'value' => '42']]],
]);
expect($attributes->postConnections())->toBe([
['post_type' => 'post', 'meta_key' => 'related_page', 'meta_value' => [42]],
['post_type' => 'product', 'meta_key' => 'featured_page', 'meta_value' => [42]],
]);
});

it('returns multiple connections when connectionPosts has multiple targets', function () {
config(['yard-query-block.connections' => [
['from' => 'post', 'to' => 'page', 'meta_key' => 'related_page'],
['from' => 'post', 'to' => 'category', 'meta_key' => 'related_category'],
]]);
$attributes = BlockAttributes::from([
'enableConnection' => true,
'connectionPosts' => [
'page' => [['label' => 'My Page', 'value' => '42']],
'category' => [['label' => 'My Category', 'value' => '10']],
],
]);
expect($attributes->postConnections())->toBe([
['post_type' => 'post', 'meta_key' => 'related_page', 'meta_value' => [42]],
['post_type' => 'post', 'meta_key' => 'related_category', 'meta_value' => [10]],
]);
});

it('skips connectionPosts entries with no matching config and returns only matched ones', function () {
config(['yard-query-block.connections' => [
['from' => 'post', 'to' => 'page', 'meta_key' => 'related_page'],
]]);
$attributes = BlockAttributes::from([
'enableConnection' => true,
'connectionPosts' => [
'page' => [['label' => 'My Page', 'value' => '42']],
'event' => [['label' => 'My Event', 'value' => '99']],
],
]);
expect($attributes->postConnections())->toBe([
['post_type' => 'post', 'meta_key' => 'related_page', 'meta_value' => [42]],
]);
});