Skip to content

Commit

Permalink
Merge pull request #1585 from craftcms/feature/1572-relation-field-el…
Browse files Browse the repository at this point in the history
…ements-matching

Feature/1572 relation field elements matching
  • Loading branch information
angrybrad authored Feb 6, 2025
2 parents a45830a + 5085685 commit a72066e
Show file tree
Hide file tree
Showing 15 changed files with 502 additions and 236 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Release Notes for Feed Me

## Unreleased

- Improved the experience of mapping relational fields with individual field instances in Craft 5. ([#1585](https://github.com/craftcms/feed-me/issues/1585))

## 6.6.1 - 2024-12-01

- Fixed a PHP error that could occur if you did not have Commerce installed when running a feed. ([#1556](https://github.com/craftcms/feed-me/issues/1556))
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function renderInlineCode(tokens, idx, options, env, renderer) {
var token = tokens[idx];

return `<code v-pre ${renderer.renderAttrs(token)}>${escapeHtml(
tokens[idx].content,
tokens[idx].content
)}</code>`;
}

Expand Down
44 changes: 44 additions & 0 deletions src/fields/Categories.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
use Cake\Utility\Hash;
use Craft;
use craft\base\Element as BaseElement;
use craft\elements\Category;
use craft\elements\Category as CategoryElement;
use craft\elements\conditions\ElementConditionInterface;
use craft\feedme\base\Field;
use craft\feedme\base\FieldInterface;
use craft\feedme\helpers\DataHelper;
use craft\feedme\helpers\FieldHelper;
use craft\feedme\models\FeedModel;
use craft\feedme\Plugin;
use craft\fields\BaseRelationField;
use craft\fields\Categories as CategoriesField;
use craft\helpers\Db;
use craft\helpers\ElementHelper;
Expand Down Expand Up @@ -215,6 +219,46 @@ public function parseField(): mixed
return $foundElements;
}

/**
* Returns an array of custom fields that can be used when querying for matching categories.
*
* If a field is passed, use the field layout linked to the source (category group) selected in the Categories field's settings.
* If the source is native (it's a category group), only return the custom fields from its layout.
* If the source is custom, return all the fields in the installation.
*
* @param FeedModel $feed
* @param BaseRelationField|null $field
* @return array
*/
public static function getMatchFields(FeedModel $feed, ?BaseRelationField $field = null): array
{
// The field will be null e.g. when importing into a categories group with maintain hierarchy turned on;
// in that case, there's the option to select a parent;
// the parent is serviced by the categories field markup too, but it doesn't tie into a custom field per se;
if ($field === null) {
$categoryGroup = Craft::$app->getCategories()->getGroupById($feed->elementGroup[Category::class]);
if (!$categoryGroup) {
return FieldHelper::getAllUniqueIdFields();
}
$fieldLayout = Craft::$app->getFields()->getLayoutById($categoryGroup->fieldLayoutId);
if (!$fieldLayout) {
return FieldHelper::getAllUniqueIdFields();
}

return array_filter($fieldLayout->getCustomFields(), fn($field) => FieldHelper::fieldCanBeUniqueId($field));
} else {
// if the Categories field has only custom source - we have no choice but return all the field
if (FieldHelper::fieldHasOnlyCustomSources($field)) {
return FieldHelper::getAllUniqueIdFields();
}
// otherwise get the layout for the group selected in the field's settings
return array_filter(
FieldHelper::getElementLayoutByField($field::class, $field),
fn($field) => FieldHelper::fieldCanBeUniqueId($field)
);
}
}

// Private Methods
// =========================================================================

Expand Down
68 changes: 68 additions & 0 deletions src/fields/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@
use Craft;
use craft\base\Element as BaseElement;
use craft\elements\conditions\ElementConditionInterface;
use craft\elements\Entry;
use craft\elements\Entry as EntryElement;
use craft\errors\ElementNotFoundException;
use craft\feedme\base\Field;
use craft\feedme\base\FieldInterface;
use craft\feedme\helpers\DataHelper;
use craft\feedme\helpers\FieldHelper;
use craft\feedme\models\FeedModel;
use craft\feedme\Plugin;
use craft\fields\BaseRelationField;
use craft\fields\Entries as EntriesField;
use craft\helpers\Db;
use craft\helpers\ElementHelper;
use craft\helpers\Json;
use craft\services\ElementSources;
use Illuminate\Support\Collection;
use Throwable;
use yii\base\Exception;

Expand Down Expand Up @@ -240,6 +245,69 @@ public function parseField(): mixed
return $foundElements;
}

/**
* Returns an array of custom fields that can be used when querying for matching entries.
*
* If a field is passed, use the field layouts linked to the sources allowed by the Entries field.
* If all the sources are native (sections), then only fields from all those sections entry types field layouts will be returned.
* If there's at least one custom source in the mix, the above list will be followed by a list of all the fields.
* If only custom sources are selected, return all the fields in the installation.
*
* @param FeedModel $feed
* @param BaseRelationField|null $field
* @return array
*/
public static function getMatchFields(FeedModel $feed, ?BaseRelationField $field = null): array
{
// The field will be null e.g. when importing into a structure section and there's the option to select a parent
// the parent is serviced by the entries field markup too, but it doesn't tie into a custom field per se;
if ($field === null) {
$entryType = Craft::$app->getEntries()->getEntryTypeById($feed->elementGroup[Entry::class]['entryType']);
if (!$entryType) {
return FieldHelper::getAllUniqueIdFields();
}

$fieldLayout = Craft::$app->getFields()->getLayoutById($entryType->fieldLayoutId);
if (!$fieldLayout) {
return FieldHelper::getAllUniqueIdFields();
}

return array_filter(
$fieldLayout->getCustomFields(),
fn($field) => FieldHelper::fieldCanBeUniqueId($field)
);
} else {
// if the Entries field has only custom sources - we have no choice but return all the field
if (FieldHelper::fieldHasOnlyCustomSources($field)) {
return FieldHelper::getAllUniqueIdFields();
}

// deal with the native sources - sections
$sections = FieldHelper::getEntrySourcesByField($field);
$entryTypes = [];
foreach ($sections as $section) {
$entryTypes = [...$entryTypes, ...$section->getEntryTypes()];
}

$allowedFields = [];
$entryTypes = Collection::make($entryTypes)->keyBy('id');

foreach ($entryTypes as $entryType) {
$fieldLayout = Craft::$app->getFields()->getLayoutById($entryType->fieldLayoutId);
$allowedFields = [...$allowedFields, ...$fieldLayout->getCustomFields()];
}

// if there's a custom source in the mix, we should add all the fields too
$customSources = array_filter($field['sources'], (fn(string $source) => str_starts_with($source, 'custom:')));

if (!empty($customSources)) {
$allowedFields = [...$allowedFields, ...Craft::$app->getFields()->getAllFields()];
}

return array_filter($allowedFields, fn($field) => FieldHelper::fieldCanBeUniqueId($field));
}
}


// Private Methods
// =========================================================================
Expand Down
16 changes: 16 additions & 0 deletions src/fields/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
use craft\feedme\base\Field;
use craft\feedme\base\FieldInterface;
use craft\feedme\helpers\DataHelper;
use craft\feedme\helpers\FieldHelper;
use craft\feedme\models\FeedModel;
use craft\feedme\Plugin;
use craft\fields\BaseRelationField;
use craft\fields\Users as UsersField;
use craft\helpers\Db;
use craft\helpers\ElementHelper;
Expand Down Expand Up @@ -209,6 +212,19 @@ public function parseField(): mixed
return $foundElements;
}

/**
* Returns an array of custom fields that can be used when querying for matching users.
* There's only one user layout, so the fields from it are returned.
*
* @param FeedModel $feed
* @param BaseRelationField|null $field
* @return array
*/
public static function getMatchFields(FeedModel $feed, ?BaseRelationField $field = null): array
{
return array_filter(FieldHelper::getUserLayoutByField(), fn($field) => FieldHelper::fieldCanBeUniqueId($field));
}

// Private Methods
// =========================================================================

Expand Down
Loading

0 comments on commit a72066e

Please sign in to comment.