Adding the "Case" entity is a problem because it's a reserved php keyword.
So we need to be able to have entity names that don't match their classname.
This function gives us a way to get api class names without guessing from the entity name.
*/
namespace Civi\API;
+use Civi\Api4\Utils\CoreUtil;
+
/**
* Class Request
* @package Civi\API
$apiRequest = \Civi\Api4\CustomValue::$action(substr($entity, 7));
}
else {
- $callable = ["\\Civi\\Api4\\$entity", $action];
+ $callable = [CoreUtil::getApiClass($entity), $action];
if (!is_callable($callable)) {
throw new \Civi\API\Exception\NotImplementedException("API ($entity, $action) does not exist (join the API team and implement it!)");
}
namespace Civi\Api4\Action\Entity;
use Civi\Api4\CustomGroup;
+use Civi\Api4\CustomValue;
use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
+use Civi\Api4\Utils\CoreUtil;
/**
* Get the names & docblocks of all APIv4 entities.
foreach (glob("$dir/*.php") as $file) {
$matches = [];
preg_match('/(\w*)\.php$/', $file, $matches);
- $entity = '\Civi\Api4\\' . $matches[1];
- if (
- (!$toGet || in_array($matches[1], $toGet))
- && is_a($entity, '\Civi\Api4\Generic\AbstractEntity', TRUE)
- ) {
- $info = $entity::getInfo();
+ $className = '\Civi\Api4\\' . $matches[1];
+ if (is_a($className, '\Civi\Api4\Generic\AbstractEntity', TRUE)) {
+ $info = $className::getInfo();
+ $entityName = $info['name'];
+ $daoName = $info['dao'] ?? NULL;
// Only include DAO entities from enabled components
- if (empty($info['dao']) || !defined($info['dao'] . '::COMPONENT') || in_array(constant($info['dao'] . '::COMPONENT'), $enabledComponents)) {
+ if ((!$toGet || in_array($entityName, $toGet)) &&
+ (!$daoName || !defined("{$daoName}::COMPONENT") || in_array($daoName::COMPONENT, $enabledComponents))
+ ) {
$entities[$info['name']] = $info;
}
}
->setSelect(['name', 'title', 'help_pre', 'help_post', 'extends', 'icon'])
->setCheckPermissions(FALSE)
->execute();
+ $baseInfo = CustomValue::getInfo();
foreach ($customEntities as $customEntity) {
$fieldName = 'Custom_' . $customEntity['name'];
- $baseEntity = '\Civi\Api4\\' . CustomGroupJoinable::getEntityFromExtends($customEntity['extends']);
+ $baseEntity = CoreUtil::getApiClass(CustomGroupJoinable::getEntityFromExtends($customEntity['extends']));
$entities[$fieldName] = [
'name' => $fieldName,
'title' => $customEntity['title'],
'title_plural' => $customEntity['title'],
'description' => ts('Custom group for %1', [1 => $baseEntity::getInfo()['title_plural']]),
- 'searchable' => TRUE,
- 'type' => ['CustomValue'],
'paths' => [
'view' => "civicrm/contact/view/cd?reset=1&gid={$customEntity['id']}&recId=[id]&multiRecordDisplay=single",
],
- 'see' => [
- 'https://docs.civicrm.org/user/en/latest/organising-your-data/creating-custom-fields/#multiple-record-fieldsets',
- '\\Civi\\Api4\\CustomGroup',
- ],
- 'icon' => $customEntity['icon'],
- ];
+ 'icon' => $customEntity['icon'] ?: NULL,
+ ] + $baseInfo;
if (!empty($customEntity['help_pre'])) {
$entities[$fieldName]['comment'] = $this->plainTextify($customEntity['help_pre']);
}
foreach ($schema->getTables() as $table) {
$entity = CoreUtil::getApiNameFromTableName($table->getName());
// Since this is an api function, exclude tables that don't have an api
- if (strpos($entity, 'Custom_') === 0 || class_exists('\Civi\Api4\\' . $entity)) {
+ if ($entity) {
$item = [
'entity' => $entity,
'table' => $table->getName(),
* @copyright CiviCRM LLC https://civicrm.org/licensing
*/
-
namespace Civi\Api4;
/**
}
/**
- * @inheritDoc
+ * @see \Civi\Api4\Generic\AbstractEntity::permissions()
+ * @return array
*/
public static function permissions() {
$entity = 'contact';
return \CRM_Utils_Array::value($entity, $permissions, []) + $permissions['default'];
}
+ /**
+ * @see \Civi\Api4\Generic\AbstractEntity::getInfo()
+ * @return array
+ */
+ public static function getInfo() {
+ return [
+ 'class' => __CLASS__,
+ 'type' => ['CustomValue'],
+ 'searchable' => TRUE,
+ 'see' => [
+ 'https://docs.civicrm.org/user/en/latest/organising-your-data/creating-custom-fields/#multiple-record-fieldsets',
+ '\Civi\Api4\CustomGroup',
+ ],
+ ];
+ }
+
}
namespace Civi\Api4\Generic;
+use Civi\Api4\Utils\CoreUtil;
use Civi\Api4\Utils\FormattingUtil;
use Civi\Api4\Utils\ReflectionUtils;
* @return array
*/
public function getPermissions() {
- $permissions = call_user_func(["\\Civi\\Api4\\" . $this->_entityName, 'permissions']);
+ $permissions = call_user_func([CoreUtil::getApiClass($this->_entityName), 'permissions']);
$permissions += [
// applies to getFields, getActions, etc.
'meta' => ['access CiviCRM'],
*/
public static function __callStatic($action, $args) {
$entity = self::getEntityName();
+ $nameSpace = str_replace('Civi\Api4\\', 'Civi\Api4\Action\\', static::class);
// Find class for this action
- $entityAction = "\\Civi\\Api4\\Action\\$entity\\" . ucfirst($action);
+ $entityAction = "$nameSpace\\" . ucfirst($action);
if (class_exists($entityAction)) {
$actionObject = new $entityAction($entity, $action);
if (isset($args[0]) && $args[0] === FALSE) {
'title_plural' => static::getEntityTitle(TRUE),
'type' => [self::stripNamespace(get_parent_class(static::class))],
'paths' => static::getEntityPaths(),
+ 'class' => static::class,
];
// Add info for entities with a corresponding DAO
$dao = \CRM_Core_DAO_AllCoreTables::getFullName($info['name']);
}
// For LEFT joins, construct a subquery to link the bridge & join tables as one
else {
- $joinEntityClass = '\Civi\Api4\\' . $joinEntity;
+ $joinEntityClass = CoreUtil::getApiClass($joinEntity);
foreach ($joinEntityClass::get($this->getCheckPermissions())->entityFields() as $name => $field) {
$bridgeFields[$field['column_name']] = '`' . $joinAlias . '`.`' . $field['column_name'] . '`';
}
*/
private function getBridgeRefs(string $bridgeEntity, string $joinEntity): array {
/* @var \Civi\Api4\Generic\DAOEntity $bridgeEntityClass */
- $bridgeEntityClass = '\Civi\Api4\\' . $bridgeEntity;
+ $bridgeEntityClass = CoreUtil::getApiClass($bridgeEntity);
$bridgeInfo = $bridgeEntityClass::getInfo();
$bridgeFields = $bridgeInfo['bridge'] ?? [];
// Sanity check - bridge entity should declare exactly 2 FK fields
private function registerBridgeJoinFields($bridgeEntity, $joinRef, $baseRef, string $alias, string $bridgeAlias, string $side): array {
$fakeFields = [];
$bridgeFkFields = [$joinRef->getReferenceKey(), $joinRef->getTypeColumn(), $baseRef->getReferenceKey(), $baseRef->getTypeColumn()];
- $bridgeEntityClass = '\Civi\Api4\\' . $bridgeEntity;
+ $bridgeEntityClass = CoreUtil::getApiClass($bridgeEntity);
foreach ($bridgeEntityClass::get($this->getCheckPermissions())->entityFields() as $name => $field) {
if ($name === 'id' || ($side === 'INNER' && in_array($name, $bridgeFkFields, TRUE))) {
continue;
use CRM_Core_DAO_AllCoreTables as AllCoreTables;
-require_once 'api/v3/utils.php';
-
class CoreUtil {
/**
- * todo this class should not rely on api3 code
- *
* @param $entityName
*
* @return \CRM_Core_DAO|string
if ($entityName === 'CustomValue' || strpos($entityName, 'Custom_') === 0) {
return 'CRM_Core_BAO_CustomValue';
}
- return \_civicrm_api3_get_BAO($entityName);
+ $dao = self::getApiClass($entityName)::getInfo()['dao'] ?? NULL;
+ if (!$dao) {
+ return NULL;
+ }
+ $bao = str_replace("DAO", "BAO", $dao);
+ // Check if this entity actually has a BAO. Fall back on the DAO if not.
+ $file = strtr($bao, '_', '/') . '.php';
+ return stream_resolve_include_path($file) ? $bao : $dao;
+ }
+
+ /**
+ * @param $entityName
+ * @return string|\Civi\Api4\Generic\AbstractEntity
+ */
+ public static function getApiClass($entityName) {
+ if (strpos($entityName, 'Custom_') === 0) {
+ return 'Civi\Api4\CustomValue';
+ }
+ // Because "Case" is a reserved php keyword
+ $className = 'Civi\Api4\\' . ($entityName === 'Case' ? 'CiviCase' : $entityName);
+ return class_exists($className) ? $className : NULL;
}
/**
$entityName = AllCoreTables::getBriefName(AllCoreTables::getClassForTable($tableName));
if (!$entityName) {
$customGroup = \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $tableName, 'name', 'table_name');
- $entityName = $customGroup ? "Custom_$customGroup" : NULL;
+ return $customGroup ? "Custom_$customGroup" : NULL;
}
- return $entityName;
+ // Verify class exists
+ return self::getApiClass($entityName) ? $entityName : NULL;
}
/**
use Civi\Api4\Entity;
use api\v4\UnitTestCase;
+use Civi\Api4\Utils\CoreUtil;
/**
* @group headless
* @throws \API_Exception
*/
public function testConformance($entity): void {
- $entityClass = 'Civi\Api4\\' . $entity;
+ $entityClass = CoreUtil::getApiClass($entity);
$this->checkEntityInfo($entityClass);
$actions = $this->checkActions($entityClass);