mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-08-29 11:00:40 +08:00
Implement record-view routing
Signed-off-by: Dillon-Brown <dillon.brown@salesagility.com>
This commit is contained in:
parent
573fed6d2b
commit
c5ee6b4f8e
17 changed files with 610 additions and 6 deletions
|
@ -17,6 +17,11 @@ if (is_array($env = @include dirname(__DIR__) . '/.env.local.php') && (!isset($e
|
|||
(new Dotenv(false))->loadEnv(dirname(__DIR__) . '/.env');
|
||||
}
|
||||
|
||||
// Global annotations to ignore
|
||||
Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredName('query');
|
||||
Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredName('fields_array');
|
||||
Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredName('absrtact');
|
||||
|
||||
$_SERVER += $_ENV;
|
||||
$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
|
||||
$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
|
||||
|
|
|
@ -8,6 +8,9 @@ import {ListComponent} from '@views/list/list.component';
|
|||
import {LoginAuthGuard} from '@services/auth/login-auth-guard.service';
|
||||
import {BaseListResolver} from '@services/metadata/base-list.resolver';
|
||||
import {BaseModuleResolver} from '@base/services/metadata/base-module.resolver';
|
||||
import {BaseRecordResolver} from '@services/metadata/base-record.resolver';
|
||||
import {RecordComponent} from '@views/record/record.component';
|
||||
import {RecordViewGuard} from '@services/record-view/record-view-guard.service';
|
||||
|
||||
/**
|
||||
* @param {[]} segments of url
|
||||
|
@ -140,11 +143,12 @@ const routes: Routes = [
|
|||
},
|
||||
{
|
||||
path: ':module/:action/:record',
|
||||
component: ClassicViewUiComponent,
|
||||
canActivate: [AuthGuard],
|
||||
component: RecordComponent,
|
||||
canActivate: [AuthGuard, RecordViewGuard],
|
||||
runGuardsAndResolvers: 'always',
|
||||
resolve: {
|
||||
legacyUrl: ClassicViewResolver,
|
||||
view: BaseModuleResolver,
|
||||
metadata: BaseRecordResolver
|
||||
},
|
||||
data: {
|
||||
reuseRoute: false,
|
||||
|
|
|
@ -22,6 +22,7 @@ import {ModuleTitleModule} from '@components/module-title/module-title.module';
|
|||
import {ListHeaderModule} from '@components/list-header/list-header.module';
|
||||
import {ListcontainerUiModule} from '@components/list-container/list-container.module';
|
||||
import {ListModule} from '@views/list/list.module';
|
||||
import {RecordModule} from '@views/record/record.module';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ErrorInterceptor} from '@services/auth/error.interceptor';
|
||||
|
@ -56,6 +57,7 @@ import {BnNgIdleService} from 'bn-ng-idle';
|
|||
ClassicViewUiModule,
|
||||
FilterUiModule,
|
||||
ListModule,
|
||||
RecordModule,
|
||||
WidgetUiModule,
|
||||
TableUiModule,
|
||||
ModuleTitleModule,
|
||||
|
|
47
core/app/src/services/metadata/base-record.resolver.ts
Normal file
47
core/app/src/services/metadata/base-record.resolver.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {ActivatedRouteSnapshot} from '@angular/router';
|
||||
import {ModuleNameMapper} from '@services/navigation/module-name-mapper/module-name-mapper.service';
|
||||
import {ActionNameMapper} from '@services/navigation/action-name-mapper/action-name-mapper.service';
|
||||
import {SystemConfigStore} from '@base/store/system-config/system-config.store';
|
||||
import {LanguageStore} from '@base/store/language/language.store';
|
||||
import {NavigationStore} from '@base/store/navigation/navigation.store';
|
||||
import {UserPreferenceStore} from '@base/store/user-preference/user-preference.store';
|
||||
import {ThemeImagesStore} from '@base/store/theme-images/theme-images.store';
|
||||
import {AppStateStore} from '@base/store/app-state/app-state.store';
|
||||
import {MetadataStore} from '@store/metadata/metadata.store.service';
|
||||
import {BaseModuleResolver} from '@services/metadata/base-module.resolver';
|
||||
import {forkJoin, Observable} from 'rxjs';
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class BaseRecordResolver extends BaseModuleResolver {
|
||||
|
||||
constructor(
|
||||
protected systemConfigStore: SystemConfigStore,
|
||||
protected languageStore: LanguageStore,
|
||||
protected navigationStore: NavigationStore,
|
||||
protected metadataStore: MetadataStore,
|
||||
protected userPreferenceStore: UserPreferenceStore,
|
||||
protected themeImagesStore: ThemeImagesStore,
|
||||
protected moduleNameMapper: ModuleNameMapper,
|
||||
protected actionNameMapper: ActionNameMapper,
|
||||
protected appStateStore: AppStateStore,
|
||||
) {
|
||||
super(
|
||||
systemConfigStore,
|
||||
languageStore,
|
||||
navigationStore,
|
||||
userPreferenceStore,
|
||||
themeImagesStore,
|
||||
moduleNameMapper,
|
||||
actionNameMapper,
|
||||
appStateStore
|
||||
);
|
||||
}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
return forkJoin({
|
||||
base: super.resolve(route),
|
||||
metadata: this.metadataStore.load(route.params.module, this.metadataStore.getMetadataTypes()),
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {ActivatedRouteSnapshot, CanActivate, Router} from '@angular/router';
|
||||
import {Observable, throwError} from 'rxjs';
|
||||
import {catchError, map} from 'rxjs/operators';
|
||||
import {MessageService} from '@services/message/message.service';
|
||||
import {RecordViewGQL} from '@store/record-view/api.record.get';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RecordViewGuard implements CanActivate {
|
||||
|
||||
protected fieldsMetadata = {
|
||||
fields: [
|
||||
'_id',
|
||||
'id',
|
||||
'record'
|
||||
]
|
||||
};
|
||||
|
||||
constructor(
|
||||
protected message: MessageService,
|
||||
protected recordViewGQL: RecordViewGQL,
|
||||
protected router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
|
||||
return this.recordViewGQL.fetch(route.params.module, route.params.record, this.fieldsMetadata)
|
||||
.pipe(
|
||||
map(({data}) => {
|
||||
const id = data.getRecordView.record.id;
|
||||
if (id) {
|
||||
return true;
|
||||
} else {
|
||||
this.message.addDangerMessageByKey('LBL_RECORD_DOES_NOT_EXIST');
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
catchError(err => throwError(err)),
|
||||
);
|
||||
}
|
||||
}
|
46
core/app/src/store/record-view/api.record.get.ts
Normal file
46
core/app/src/store/record-view/api.record.get.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {Apollo} from 'apollo-angular';
|
||||
import gql from 'graphql-tag';
|
||||
import {Observable} from 'rxjs';
|
||||
import {ApolloQueryResult} from 'apollo-client';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RecordViewGQL {
|
||||
|
||||
constructor(private apollo: Apollo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data from backend
|
||||
*
|
||||
* @param {string} module name
|
||||
* @param {string} record id
|
||||
* @param {object} metadata with the fields to ask for
|
||||
* @returns {object} Observable<ApolloQueryResult<any>>
|
||||
*/
|
||||
public fetch(
|
||||
module: string,
|
||||
record: string,
|
||||
metadata: { fields: string[] }
|
||||
): Observable<ApolloQueryResult<any>> {
|
||||
const fields = metadata.fields;
|
||||
|
||||
const queryOptions = {
|
||||
query: gql`
|
||||
query recordView($module: String!, $record: String!) {
|
||||
getRecordView(module: $module, record: $record) {
|
||||
${fields.join('\n')}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
module,
|
||||
record,
|
||||
},
|
||||
};
|
||||
|
||||
return this.apollo.query(queryOptions);
|
||||
}
|
||||
}
|
25
core/app/src/store/record-view/record-view.store.ts
Normal file
25
core/app/src/store/record-view/record-view.store.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {AppData, ViewStore} from '@store/view/view.store';
|
||||
import {Metadata} from '@store/metadata/metadata.store.service';
|
||||
import {Observable} from 'rxjs';
|
||||
|
||||
export interface RecordViewModel {
|
||||
appData: AppData;
|
||||
metadata: Metadata;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RecordViewStore extends ViewStore {
|
||||
vm$: Observable<RecordViewModel>;
|
||||
vm: RecordViewModel;
|
||||
|
||||
clear(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean destroy
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.clear();
|
||||
}
|
||||
}
|
4
core/app/views/record/record.component.html
Normal file
4
core/app/views/record/record.component.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!-- Start Record View Section -->
|
||||
<div class="record-view" *ngIf="(vm$ | async) as vm">
|
||||
</div>
|
||||
<!-- End Record View Section -->
|
61
core/app/views/record/record.component.spec.ts
Normal file
61
core/app/views/record/record.component.spec.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {Component} from '@angular/core';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
import {DynamicModule} from 'ng-dynamic-component';
|
||||
import {FieldModule} from '@fields/field.module';
|
||||
import {DropdownButtonModule} from '@components/dropdown-button/dropdown-button.module';
|
||||
import {SortButtonModule} from '@components/sort-button/sort-button.module';
|
||||
import {RecordViewStore} from '@store/record-view/record-view.store';
|
||||
import {RecordComponent} from '@views/record/record.component';
|
||||
|
||||
@Component({
|
||||
selector: 'record-test-host-component',
|
||||
template: '<scrm-record></scrm-record>'
|
||||
})
|
||||
class RecordTestHostComponent {
|
||||
}
|
||||
|
||||
describe('RecordComponent', () => {
|
||||
let testHostComponent: RecordTestHostComponent;
|
||||
let testHostFixture: ComponentFixture<RecordTestHostComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
/* eslint-disable camelcase, @typescript-eslint/camelcase */
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
RouterTestingModule,
|
||||
BrowserAnimationsModule,
|
||||
ImageModule,
|
||||
ApolloTestingModule,
|
||||
DynamicModule,
|
||||
FieldModule,
|
||||
DropdownButtonModule,
|
||||
DropdownButtonModule,
|
||||
SortButtonModule
|
||||
],
|
||||
declarations: [RecordComponent, RecordTestHostComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: RecordViewStore
|
||||
}
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
/* eslint-enable camelcase, @typescript-eslint/camelcase */
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
testHostFixture = TestBed.createComponent(RecordTestHostComponent);
|
||||
testHostComponent = testHostFixture.componentInstance;
|
||||
testHostFixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(testHostComponent).toBeTruthy();
|
||||
});
|
||||
});
|
30
core/app/views/record/record.component.ts
Normal file
30
core/app/views/record/record.component.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {AppStateStore} from '@store/app-state/app-state.store';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {RecordViewModel, RecordViewStore} from '@store/record-view/record-view.store';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-record',
|
||||
templateUrl: './record.component.html',
|
||||
styleUrls: [],
|
||||
providers: [RecordViewStore]
|
||||
})
|
||||
export class RecordComponent implements OnInit, OnDestroy {
|
||||
recordSub: Subscription;
|
||||
vm$: Observable<RecordViewModel> = null;
|
||||
|
||||
constructor(protected appState: AppStateStore, protected recordStore: RecordViewStore) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.vm$ = this.recordStore.vm$;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.recordSub) {
|
||||
this.recordSub.unsubscribe();
|
||||
}
|
||||
|
||||
this.recordStore.destroy();
|
||||
}
|
||||
}
|
15
core/app/views/record/record.module.ts
Normal file
15
core/app/views/record/record.module.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {RecordComponent} from './record.component';
|
||||
import {FieldModule} from '@fields/field.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [RecordComponent],
|
||||
exports: [RecordComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FieldModule
|
||||
],
|
||||
})
|
||||
export class RecordModule {
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
<?php
|
||||
|
||||
// stub for record view
|
111
core/legacy/RecordViewHandler.php
Normal file
111
core/legacy/RecordViewHandler.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace SuiteCRM\Core\Legacy;
|
||||
|
||||
use App\Entity\RecordView;
|
||||
use App\Service\ModuleNameMapperInterface;
|
||||
use App\Service\RecordViewProviderInterface;
|
||||
use BeanFactory;
|
||||
use InvalidArgumentException;
|
||||
use SugarBean;
|
||||
|
||||
/**
|
||||
* Class RecordViewHandler
|
||||
* @package SuiteCRM\Core\Legacy
|
||||
*/
|
||||
class RecordViewHandler extends LegacyHandler implements RecordViewProviderInterface
|
||||
{
|
||||
public const HANDLER_KEY = 'record';
|
||||
|
||||
/**
|
||||
* @var ModuleNameMapperInterface
|
||||
*/
|
||||
private $moduleNameMapper;
|
||||
|
||||
/**
|
||||
* RecordViewHandler constructor.
|
||||
* @param string $projectDir
|
||||
* @param string $legacyDir
|
||||
* @param string $legacySessionName
|
||||
* @param string $defaultSessionName
|
||||
* @param LegacyScopeState $legacyScopeState
|
||||
* @param ModuleNameMapperInterface $moduleNameMapper
|
||||
*/
|
||||
public function __construct(
|
||||
string $projectDir,
|
||||
string $legacyDir,
|
||||
string $legacySessionName,
|
||||
string $defaultSessionName,
|
||||
LegacyScopeState $legacyScopeState,
|
||||
ModuleNameMapperInterface $moduleNameMapper
|
||||
) {
|
||||
parent::__construct($projectDir, $legacyDir, $legacySessionName, $defaultSessionName, $legacyScopeState);
|
||||
$this->moduleNameMapper = $moduleNameMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getHandlerKey(): string
|
||||
{
|
||||
return self::HANDLER_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $module
|
||||
* @param string $id
|
||||
* @return RecordView
|
||||
*/
|
||||
public function getRecord(string $module, string $id): RecordView
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$recordView = new RecordView();
|
||||
$moduleName = $this->validateModuleName($module);
|
||||
$bean = BeanFactory::getBean($moduleName, $id);
|
||||
|
||||
if (!$bean) {
|
||||
$bean = $this->newBeanSafe($moduleName);
|
||||
}
|
||||
|
||||
$recordView->setId($id);
|
||||
$recordView->setRecord((array)$bean);
|
||||
|
||||
$this->close();
|
||||
|
||||
return $recordView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $module
|
||||
*
|
||||
* @return SugarBean
|
||||
* @throws InvalidArgumentException When the module is invalid.
|
||||
*/
|
||||
private function newBeanSafe($module): SugarBean
|
||||
{
|
||||
$bean = BeanFactory::newBean($module);
|
||||
|
||||
if (!$bean instanceof SugarBean) {
|
||||
throw new InvalidArgumentException(sprintf('Module %s does not exist', $module));
|
||||
}
|
||||
|
||||
return $bean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $moduleName
|
||||
* @return string
|
||||
*/
|
||||
private function validateModuleName($moduleName): string
|
||||
{
|
||||
$moduleName = $this->moduleNameMapper->toLegacy($moduleName);
|
||||
|
||||
if (!$this->moduleNameMapper->isValidModule($moduleName)) {
|
||||
throw new InvalidArgumentException('Invalid module name: ' . $moduleName);
|
||||
}
|
||||
|
||||
return $moduleName;
|
||||
}
|
||||
}
|
59
core/src/DataProvider/RecordViewItemDataProvider.php
Normal file
59
core/src/DataProvider/RecordViewItemDataProvider.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\DataProvider;
|
||||
|
||||
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
|
||||
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
|
||||
use App\Entity\RecordView;
|
||||
use App\Service\RecordViewProviderInterface;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class RecordViewItemDataProvider
|
||||
*/
|
||||
final class RecordViewItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var RecordViewProviderInterface
|
||||
*/
|
||||
private $recordViewHandler;
|
||||
|
||||
/**
|
||||
* RecordViewItemDataProvider constructor.
|
||||
* @param RecordViewProviderInterface $recordViewHandler
|
||||
*/
|
||||
public function __construct(RecordViewProviderInterface $recordViewHandler)
|
||||
{
|
||||
$this->recordViewHandler = $recordViewHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined supported resources
|
||||
* @param string $resourceClass
|
||||
* @param string|null $operationName
|
||||
* @param array $context
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
|
||||
{
|
||||
return RecordView::class === $resourceClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get get record by id
|
||||
* @param string $resourceClass
|
||||
* @param array|int|string $id
|
||||
* @param string|null $operationName
|
||||
* @param array $context
|
||||
* @return RecordView|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getItem(
|
||||
string $resourceClass,
|
||||
$id,
|
||||
string $operationName = null,
|
||||
array $context = []
|
||||
): ?RecordView {
|
||||
return $this->recordViewHandler->getRecord($id);
|
||||
}
|
||||
}
|
96
core/src/Entity/RecordView.php
Normal file
96
core/src/Entity/RecordView.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Core\Annotation\ApiResource;
|
||||
use ApiPlatform\Core\Annotation\ApiProperty;
|
||||
use App\Resolver\RecordViewResolver;
|
||||
|
||||
/**
|
||||
* @ApiResource(
|
||||
* attributes={"security"="is_granted('ROLE_USER')"},
|
||||
* itemOperations={
|
||||
* "get"={"path"="/record/{id}"}
|
||||
* },
|
||||
* collectionOperations={},
|
||||
* graphql={
|
||||
* "get"={
|
||||
* "item_query"=RecordViewResolver::class,
|
||||
* "args"={
|
||||
* "module"={"type"="String!"},
|
||||
* "record"={"type"="String!"},
|
||||
* }
|
||||
* },
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class RecordView
|
||||
{
|
||||
/**
|
||||
* The record ID
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @ApiProperty(
|
||||
* identifier=true,
|
||||
* attributes={
|
||||
* "openapi_context"={
|
||||
* "type"="string",
|
||||
* "description"="The record ID.",
|
||||
* }
|
||||
* },
|
||||
*
|
||||
* )
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* RecordView data
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @ApiProperty(
|
||||
* attributes={
|
||||
* "openapi_context"={
|
||||
* "type"="array",
|
||||
* "description"="The record-view data",
|
||||
* },
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
public $record;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*/
|
||||
public function setId($id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get RecordView record
|
||||
* @return array
|
||||
*/
|
||||
public function getRecord(): ?array
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set RecordView record
|
||||
* @param array $record
|
||||
*/
|
||||
public function setRecord(array $record): void
|
||||
{
|
||||
$this->record = $record;
|
||||
}
|
||||
}
|
41
core/src/Resolver/RecordViewResolver.php
Normal file
41
core/src/Resolver/RecordViewResolver.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Resolver;
|
||||
|
||||
use ApiPlatform\Core\GraphQl\Resolver\QueryItemResolverInterface;
|
||||
use App\Entity\RecordView;
|
||||
use Exception;
|
||||
use SuiteCRM\Core\Legacy\RecordViewHandler;
|
||||
|
||||
class RecordViewResolver implements QueryItemResolverInterface
|
||||
{
|
||||
/**
|
||||
* @var RecordViewHandler
|
||||
*/
|
||||
protected $recordViewHandler;
|
||||
|
||||
/**
|
||||
* RecordViewResolver constructor.
|
||||
* @param RecordViewHandler $recordViewHandler
|
||||
*/
|
||||
public function __construct(RecordViewHandler $recordViewHandler)
|
||||
{
|
||||
$this->recordViewHandler = $recordViewHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecordView|null $item
|
||||
*
|
||||
* @param array $context
|
||||
* @return RecordView
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __invoke($item, array $context): RecordView
|
||||
{
|
||||
|
||||
$module = $context['args']['module'] ?? '';
|
||||
$record = $context['args']['record'] ?? '';
|
||||
|
||||
return $this->recordViewHandler->getRecord($module, $record);
|
||||
}
|
||||
}
|
18
core/src/Service/RecordViewProviderInterface.php
Normal file
18
core/src/Service/RecordViewProviderInterface.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\RecordView;
|
||||
use Exception;
|
||||
|
||||
interface RecordViewProviderInterface
|
||||
{
|
||||
/**
|
||||
* Get record
|
||||
* @param string $module
|
||||
* @param string $id
|
||||
* @return RecordView
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getRecord(string $module, string $id): RecordView;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue