-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
ContextFactory.php
155 lines (145 loc) · 5.13 KB
/
ContextFactory.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.5.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\View\Form;
use Cake\Collection\Collection;
use Cake\Datasource\EntityInterface;
use Cake\Form\Form;
use Cake\Http\ServerRequest;
use RuntimeException;
use Traversable;
/**
* Factory for getting form context instance based on provided data.
*/
class ContextFactory
{
/**
* Context providers.
*
* @var array
*/
protected $providers = [];
/**
* Constructor.
*
* @param array $providers Array of provider callables. Each element should
* be of form `['type' => 'a-string', 'callable' => ..]`
*/
public function __construct(array $providers = [])
{
foreach ($providers as $provider) {
$this->addProvider($provider['type'], $provider['callable']);
}
}
/**
* Create factory instance with providers "array", "form" and "orm".
*
* @param array $providers Array of provider callables. Each element should
* be of form `['type' => 'a-string', 'callable' => ..]`
* @return \Cake\View\Form\ContextFactory
*/
public static function createWithDefaults(array $providers = [])
{
$providers = [
[
'type' => 'orm',
'callable' => function ($request, $data) {
if (is_array($data['entity']) || $data['entity'] instanceof Traversable) {
$pass = (new Collection($data['entity']))->first() !== null;
if ($pass) {
return new EntityContext($request, $data);
}
}
if ($data['entity'] instanceof EntityInterface) {
return new EntityContext($request, $data);
}
if (is_array($data['entity']) && empty($data['entity']['schema'])) {
return new EntityContext($request, $data);
}
}
],
[
'type' => 'array',
'callable' => function ($request, $data) {
if (is_array($data['entity']) && isset($data['entity']['schema'])) {
return new ArrayContext($request, $data['entity']);
}
}
],
[
'type' => 'form',
'callable' => function ($request, $data) {
if ($data['entity'] instanceof Form) {
return new FormContext($request, $data);
}
}
],
] + $providers;
return new static($providers);
}
/**
* Add a new context type.
*
* Form context types allow FormHelper to interact with
* data providers that come from outside CakePHP. For example
* if you wanted to use an alternative ORM like Doctrine you could
* create and connect a new context class to allow FormHelper to
* read metadata from doctrine.
*
* @param string $type The type of context. This key
* can be used to overwrite existing providers.
* @param callable $check A callable that returns an object
* when the form context is the correct type.
* @return $this
*/
public function addProvider($type, callable $check)
{
$this->providers = [$type => ['type' => $type, 'callable' => $check]]
+ $this->providers;
return $this;
}
/**
* Find the matching context for the data.
*
* If no type can be matched a NullContext will be returned.
*
* @param \Cake\Http\ServerRequest $request Request instance.
* @param array $data The data to get a context provider for.
* @return \Cake\View\Form\ContextInterface Context provider.
* @throws \RuntimeException when the context class does not implement the
* ContextInterface.
*/
public function get(ServerRequest $request, array $data = [])
{
$data += ['entity' => null];
foreach ($this->providers as $provider) {
$check = $provider['callable'];
$context = $check($request, $data);
if ($context) {
break;
}
}
if (!isset($context)) {
$context = new NullContext($request, $data);
}
if (!($context instanceof ContextInterface)) {
throw new RuntimeException(sprintf(
'Context providers must return object implementing %s. Got "%s" instead.',
ContextInterface::class,
getTypeName($context)
));
}
return $context;
}
}