Skip to content

Feature/cms 1221 add a search input to the fields and entry types pages api mode #14136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
entry types search (simple approach)
  • Loading branch information
i-just committed Jan 15, 2024
commit ac154a7315aa3ef32e77d0985c1f3baee7bb8b29
1 change: 0 additions & 1 deletion src/config/cproutes/common.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
'settings/sections' => 'sections/index',
'settings/sections/new' => 'sections/edit-section',
'settings/sections/<sectionId:\d+>' => 'sections/edit-section',
'settings/entry-types' => 'entry-types/index',
'settings/entry-types/new' => 'entry-types/edit',
'settings/entry-types/<entryTypeId:\d+>' => 'entry-types/edit',
'settings/sites' => 'sites/settings-index',
Expand Down
76 changes: 24 additions & 52 deletions src/controllers/EntryTypesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
namespace craft\controllers;

use Craft;
use craft\base\ElementContainerFieldInterface;
use craft\elements\Entry;
use craft\helpers\UrlHelper;
use craft\models\EntryType;
use craft\models\Section;
use craft\web\Controller;
Expand Down Expand Up @@ -39,56 +37,6 @@ public function beforeAction($action): bool
return parent::beforeAction($action);
}

/**
* Entry types index
*
* @return Response
*/
public function actionIndex(): Response
{
$sectionsService = Craft::$app->getEntries();
$entryTypes = $sectionsService->getAllEntryTypes();
usort($entryTypes, fn(EntryType $a, EntryType $b) => Craft::t('site', $a->name) <=> Craft::t('site', $b->name));

$entryTypeUsages = [];

// Sections
foreach (Craft::$app->getEntries()->getAllSections() as $section) {
foreach ($section->getEntryTypes() as $entryType) {
$entryTypeUsages[$entryType->id][] = [
'section',
Craft::t('site', $section->name),
$section->getCpEditUrl(),
];
}
}

// Fields
foreach (Craft::$app->getFields()->getAllFields() as $field) {
if ($field instanceof ElementContainerFieldInterface) {
foreach ($field->getFieldLayoutProviders() as $provider) {
if ($provider instanceof EntryType) {
$entryTypeUsages[$provider->id][] = [
'field',
Craft::t('site', $field->name),
UrlHelper::cpUrl("settings/fields/edit/$field->id"),
];
}
}
}
}

// sort by name
foreach ($entryTypeUsages as &$usages) {
usort($usages, fn($a, $b) => $a[1] <=> $b[1]);
}

return $this->renderTemplate('settings/entry-types/_index.twig', [
'entryTypes' => $entryTypes,
'entryTypeUsages' => $entryTypeUsages,
]);
}

/**
* Edit an entry type
*
Expand Down Expand Up @@ -198,4 +146,28 @@ public function actionDelete(): Response
$success = Craft::$app->getEntries()->deleteEntryTypeById($entryTypeId);
return $success ? $this->asSuccess() : $this->asFailure();
}

/**
* Returns data formatted for AdminTable vue component
*
* @return Response
* @throws BadRequestHttpException
*/
public function actionTableData(): Response
{
$this->requireAcceptsJson();

$entriesService = Craft::$app->getEntries();

$page = $this->request->getParam('page', 1);
$limit = $this->request->getParam('per_page', 100);
$searchTerm = $this->request->getParam('search', null);

[$pagination, $tableData] = $entriesService->getTableData($page, $limit, $searchTerm);

return $this->asSuccess(data: [
'pagination' => $pagination,
'data' => $tableData,
]);
}
}
122 changes: 122 additions & 0 deletions src/services/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Craft;
use craft\base\Element;
use craft\base\ElementContainerFieldInterface;
use craft\base\Field;
use craft\base\MemoizableArray;
use craft\db\Query;
Expand All @@ -21,12 +22,14 @@
use craft\events\DeleteSiteEvent;
use craft\events\EntryTypeEvent;
use craft\events\SectionEvent;
use craft\helpers\AdminTable;
use craft\helpers\ArrayHelper;
use craft\helpers\Db;
use craft\helpers\Json;
use craft\helpers\ProjectConfig as ProjectConfigHelper;
use craft\helpers\Queue;
use craft\helpers\StringHelper;
use craft\helpers\UrlHelper;
use craft\i18n\Translation;
use craft\models\EntryType;
use craft\models\FieldLayout;
Expand Down Expand Up @@ -1620,6 +1623,125 @@ public function refreshEntryTypes(): void
$this->_entryTypes = null;
}

/**
* Returns data for vue AdminTable (pagination and tableData).
*
* @param int $page
* @param int $limit
* @param string|null $searchTerm
* @return array
*/
public function getTableData(int $page, int $limit, ?string $searchTerm = null): array
{
$searchTerm = $searchTerm ? trim($searchTerm) : $searchTerm;

$offset = ($page - 1) * $limit;
$query = $this->_createEntryTypeQuery();

if ($searchTerm !== null && $searchTerm !== '') {
$searchParams = $this->_getSearchParams($searchTerm);
if (!empty($searchParams)) {
$query->where(['or', ...$searchParams]);
}
}

$total = $query->count();

$query->limit($limit);
$query->offset($offset);

$results = $query->all();

$entryTypes = array_values(array_filter(
array_map(fn(array $result) => $this->_entryTypes()->firstWhere('id', $result['id']), $results)
));

usort($entryTypes,
fn(EntryType $a, EntryType $b) => Craft::t('site', $a->name) <=> Craft::t('site', $b->name)
);

$entryTypeUsages = $this->getEntryTypeUsages();

$tableData = [];
foreach ($entryTypes as $entryType) {
$tableData[] = [
'id' => $entryType->id,
'title' => Craft::t('site', $entryType->name),
'url' => $entryType->getCpEditUrl(),
'name' => Craft::t('site', $entryType->name), //|e,
'handle' => $entryType->handle,
'usages' => $entryTypeUsages[$entryType->id],
];
}

$pagination = AdminTable::paginationLinks($page, $total, $limit);

return [$pagination, $tableData];
}

/**
* Returns an array of sections and fields where entry type is used.
*
* @return array
*/
protected function getEntryTypeUsages(): array
{
$entryTypeUsages = [];

// Sections
foreach ($this->getAllSections() as $section) {
foreach ($section->getEntryTypes() as $entryType) {
$entryTypeUsages[$entryType->id][] = [
'section',
Craft::t('site', $section->name),
$section->getCpEditUrl(),
];
}
}

// Fields
foreach (Craft::$app->getFields()->getAllFields() as $field) {
if ($field instanceof ElementContainerFieldInterface) {
foreach ($field->getFieldLayoutProviders() as $provider) {
if ($provider instanceof EntryType) {
$entryTypeUsages[$provider->id][] = [
'field',
Craft::t('site', $field->name),
UrlHelper::cpUrl("settings/fields/edit/$field->id"),
];
}
}
}
}

// sort by name
foreach ($entryTypeUsages as &$usages) {
usort($usages, fn($a, $b) => $a[1] <=> $b[1]);
}

return $entryTypeUsages;
}

/**
* Returns the sql expression to be used in the 'where' param for the query.
*
* @param string $term
* @return array
*/
private function _getSearchParams(string $term): array
{
$searchParams = ['name', 'handle'];
$searchQueries = [];

if ($term !== '') {
foreach ($searchParams as $param) {
$searchQueries[] = ['like', $param, '%' . $term . '%', false];
}
}

return $searchQueries;
}

/**
* Gets an entry type's record by uid.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,29 @@
<div id="entrytypes-vue-admin-table"></div>
{% endblock %}

{% set tableData = entryTypes|map(entryType => {
id: entryType.id,
title: entryType.name|t('site'),
url: entryType.getCpEditUrl(),
name: entryType.name|t('site')|e,
handle: entryType.handle,
usages: tag('ul', {
html: (entryTypeUsages[entryType.id] ?? [])|map(usage => tag('li', {
html: tag('a', {
text: usage[1],
href: usage[2],
class: 'iconlink',
data: {
icon: usage[0],
}
}),
}))|join(''),
}),
}) %}

{% js %}
var columns = [
{ name: '__slot:title', title: Craft.t('app', 'Name') },
{ name: '__slot:handle', title: Craft.t('app', 'Handle') },
{ name: 'usages', title: Craft.t('app', 'Used by') },
{
name: 'usages',
title: Craft.t('app', 'Used by'),
callback: value => {
if (!value) {
return null;
}

let list = `<ul>`;
for (let i = 0; i < value.length; i++) {
list += `<li>`;
list += `<a href="${value[i][2]}" class="iconlink" data-icon="${value[i][0]}">${value[i][1]}</a>`
list += `</li>`;
}
list += `</ul>`;

return list;
}
},
];

new Craft.VueAdminTable({
Expand All @@ -61,6 +59,7 @@ new Craft.VueAdminTable({
deleteAction: 'entry-types/delete',
deleteConfirmationMessage: Craft.t('app', 'Are you sure you want to delete “{name}” and all entries of that type?'),
emptyMessage: Craft.t('app', 'No entry types exist yet.'),
tableData: {{ tableData|json_encode|raw }},
tableDataEndpoint: 'entry-types/table-data',
search: true,
});
{% endjs %}