namespace Civi\Api4\Utils;
+use Civi\API\Exception\NotImplementedException;
use Civi\API\Request;
use Civi\Api4\Entity;
use Civi\Api4\Event\CreateApi4RequestEvent;
return $schemaMap;
}
+ /**
+ * Fetches database references + those returned by hook
+ *
+ * @see \CRM_Utils_Hook::referenceCounts()
+ * @param string $entityName
+ * @param int $entityId
+ * @return array{name: string, type: string, count: int, table: string|null, key: string|null}[]
+ * @throws NotImplementedException
+ */
+ public static function getRefCount(string $entityName, $entityId) {
+ $daoName = self::getInfoItem($entityName, 'dao');
+ if (!$daoName) {
+ throw new NotImplementedException("Cannot getRefCount for $entityName - dao not found.");
+ }
+ /** @var \CRM_Core_DAO $dao */
+ $dao = new $daoName();
+ $dao->id = $entityId;
+ return $dao->getReferenceCounts();
+ }
+
}
->addWhere('search_displays', 'CONTAINS', $display['saved_search_id.name'] . ".{$display['name']}")
->execute();
}
+ // When deleting a savedSearch, delete any Afforms which use the default display
+ if ($entity === 'SearchDisplay' && $op === 'delete') {
+ $search = \Civi\Api4\SavedSearch::get(FALSE)
+ ->addSelect('name')
+ ->addWhere('id', '=', $id)
+ ->execute()->first();
+ \Civi\Api4\Afform::revert(FALSE)
+ ->addWhere('search_displays', 'CONTAINS', $search['name'])
+ ->execute();
+ }
+}
+
+/**
+ * Implements hook_civicrm_referenceCounts().
+ */
+function afform_civicrm_referenceCounts($dao, &$counts) {
+ // Count afforms which contain a search display
+ if (is_a($dao, 'CRM_Search_DAO_SearchDisplay') && $dao->id) {
+ if (empty($dao->saved_search_id) || empty($dao->name)) {
+ $dao->find(TRUE);
+ }
+ $search = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $dao->saved_search_id);
+ $afforms = \Civi\Api4\Afform::get(FALSE)
+ ->selectRowCount()
+ ->addWhere('search_displays', 'CONTAINS', "$search.$dao->name")
+ ->execute();
+ if ($afforms->count()) {
+ $counts[] = [
+ 'name' => 'Afform',
+ 'type' => 'Afform',
+ 'count' => $afforms->count(),
+ ];
+ }
+ }
+ // Count afforms which contain any displays from a SavedSearch (including the default display)
+ elseif (is_a($dao, 'CRM_Contact_DAO_SavedSearch') && $dao->id) {
+ if (empty($dao->name)) {
+ $dao->find(TRUE);
+ }
+ $clauses = [
+ ['search_displays', 'CONTAINS', $dao->name],
+ ];
+ try {
+ $displays = civicrm_api4('SearchDisplay', 'get', [
+ 'where' => [['saved_search_id', '=', $dao->id]],
+ 'select' => 'name',
+ ], ['name']);
+ foreach ($displays as $displayName) {
+ $clauses[] = ['search_displays', 'CONTAINS', $dao->name . '.' . $displayName];
+ }
+ }
+ catch (Exception $e) {
+ // In case SearchKit is not installed, the api call would fail
+ }
+ $afforms = \Civi\Api4\Afform::get(FALSE)
+ ->selectRowCount()
+ ->addClause('OR', $clauses)
+ ->execute();
+ if ($afforms->count()) {
+ $counts[] = [
+ 'name' => 'Afform',
+ 'type' => 'Afform',
+ 'count' => $afforms->count(),
+ ];
+ }
+ }
}
// Wordpress only: Register callback for rendering shortcodes
use Civi\Api4\Email;
use Civi\Api4\SavedSearch;
use Civi\Api4\SearchDisplay;
+use Civi\Api4\Utils\CoreUtil;
use Civi\Test\HeadlessInterface;
use Civi\Test\TransactionalInterface;
->apply();
}
+ public function tearDown() {
+ Afform::revert(FALSE)->addWhere('has_local', '=', TRUE)->execute();
+ parent::tearDown();
+ }
+
/**
* Test running a searchDisplay within an afform.
*/
$this->assertCount(1, $result);
}
- public function testDeleteSearchWillDeleteAfform() {
+ public function testSearchReferencesToAfform() {
$search = SavedSearch::create(FALSE)
->setValues([
'name' => 'TestSearchToDelete',
])
->execute()->first();
+ // The search should have one reference (its display)
+ $refs = CoreUtil::getRefCount('SavedSearch', $search['id']);
+ $this->assertCount(1, $refs);
+
+ // The display should have zero references
+ $refs = CoreUtil::getRefCount('SearchDisplay', $display['id']);
+ $this->assertCount(0, $refs);
+
Afform::create(FALSE)
->addValue('name', 'TestAfformToDelete')
->addValue('title', 'TestAfformToDelete')
->addValue('layout', '<div><crm-search-display-table search-name="TestSearchToDelete" display-name="TestDisplayToDelete"></crm-search-display-table></div>')
->execute();
- $this->assertCount(1, Afform::get(FALSE)->addWhere('name', '=', 'TestAfformToDelete')->execute());
+ $this->assertCount(1, Afform::get(FALSE)->addWhere('search_displays', 'CONTAINS', 'TestSearchToDelete.TestDisplayToDelete')->execute());
+
+ // The search should now have two references (its display + Afform)
+ $refs = CoreUtil::getRefCount('SavedSearch', $search['id']);
+ $this->assertCount(2, $refs);
+
+ // The display should now have one reference (the Afform)
+ $refs = CoreUtil::getRefCount('SearchDisplay', $display['id']);
+ $this->assertCount(1, $refs);
+ $this->assertEquals('Afform', $refs[0]['type']);
+
+ // Create an afform that uses the search default display
+ Afform::create(FALSE)
+ ->addValue('name', 'TestAfformToDelete2')
+ ->addValue('title', 'TestAfformToDelete2')
+ ->setLayoutFormat('html')
+ ->addValue('layout', '<div><crm-search-display-table search-name="TestSearchToDelete"></crm-search-display-table></div>')
+ ->execute();
+
+ $this->assertCount(1, Afform::get(FALSE)->addWhere('search_displays', 'CONTAINS', 'TestSearchToDelete')->execute());
+ $this->assertCount(2, Afform::get(FALSE)->addWhere('name', 'CONTAINS', 'TestAfformToDelete')->execute());
+
+ // The search should now have three references (its display + 2 Afforms)
+ $refs = CoreUtil::getRefCount('SavedSearch', $search['id']);
+ $this->assertCount(2, $refs);
+ $this->assertEquals(2, array_column($refs, 'count', 'type')['Afform']);
+
+ // The display should still have one reference (the Afform)
+ $refs = CoreUtil::getRefCount('SearchDisplay', $display['id']);
+ $this->assertCount(1, $refs);
+ $this->assertEquals('Afform', $refs[0]['type']);
SavedSearch::delete(FALSE)
->addWhere('name', '=', 'TestSearchToDelete')
->execute();
- $this->assertCount(0, Afform::get(FALSE)->addWhere('name', '=', 'TestAfformToDelete')->execute());
+ $this->assertCount(0, Afform::get(FALSE)->addWhere('search_displays', 'CONTAINS', 'TestSearchToDelete.TestDisplayToDelete')->execute());
+ $this->assertCount(0, Afform::get(FALSE)->addWhere('name', 'CONTAINS', 'TestAfformToDelete')->execute());
}
}