12
12
13
13
abstract class Component
14
14
{
15
+ /**
16
+ * The properties / methods that should not be exposed to the component.
17
+ *
18
+ * @var array
19
+ */
20
+ protected $ except = [];
21
+
22
+ /**
23
+ * The component alias name.
24
+ *
25
+ * @var string
26
+ */
27
+ public $ componentName ;
28
+
29
+ /**
30
+ * The component attributes.
31
+ *
32
+ * @var \Illuminate\View\ComponentAttributeBag
33
+ */
34
+ public $ attributes ;
35
+
36
+ /**
37
+ * The view factory instance, if any.
38
+ *
39
+ * @var \Illuminate\Contracts\View\Factory|null
40
+ */
41
+ protected static $ factory ;
42
+
43
+ /**
44
+ * The component resolver callback.
45
+ *
46
+ * @var (\Closure(string, array): Component)|null
47
+ */
48
+ protected static $ componentsResolver ;
49
+
50
+ /**
51
+ * The cache of blade view names, keyed by contents.
52
+ *
53
+ * @var array<string, string>
54
+ */
55
+ protected static $ bladeViewCache = [];
56
+
15
57
/**
16
58
* The cache of public property names, keyed by class.
17
59
*
@@ -27,32 +69,61 @@ abstract class Component
27
69
protected static $ methodCache = [];
28
70
29
71
/**
30
- * The properties / methods that should not be exposed to the component .
72
+ * The cache of constructor parameters, keyed by class .
31
73
*
32
- * @var array
74
+ * @var array<class-string, array<int, string>>
33
75
*/
34
- protected $ except = [];
76
+ protected static $ constructorParametersCache = [];
35
77
36
78
/**
37
- * The component alias name .
79
+ * Get the view / view contents that represent the component .
38
80
*
39
- * @var string
81
+ * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\Support\Htmlable|\Closure| string
40
82
*/
41
- public $ componentName ;
83
+ abstract public function render () ;
42
84
43
85
/**
44
- * The component attributes .
86
+ * Resolve the component instance with the given data .
45
87
*
46
- * @var \Illuminate\View\ComponentAttributeBag
88
+ * @param array $data
89
+ * @return static
47
90
*/
48
- public $ attributes ;
91
+ public static function resolve ($ data )
92
+ {
93
+ if (static ::$ componentsResolver ) {
94
+ return call_user_func (static ::$ componentsResolver , static ::class, $ data );
95
+ }
96
+
97
+ $ parameters = static ::extractConstructorParameters ();
98
+
99
+ $ dataKeys = array_keys ($ data );
100
+
101
+ if (empty (array_diff ($ parameters , $ dataKeys ))) {
102
+ return new static (...array_intersect_key ($ data , array_flip ($ parameters )));
103
+ }
104
+
105
+ return Container::getInstance ()->make (static ::class, $ data );
106
+ }
49
107
50
108
/**
51
- * Get the view / view contents that represent the component.
109
+ * Extract the constructor parameters for the component.
52
110
*
53
- * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\Support\Htmlable|\Closure|string
111
+ * @return array
54
112
*/
55
- abstract public function render ();
113
+ protected static function extractConstructorParameters ()
114
+ {
115
+ if (! isset (static ::$ constructorParametersCache [static ::class])) {
116
+ $ class = new ReflectionClass (static ::class);
117
+
118
+ $ constructor = $ class ->getConstructor ();
119
+
120
+ static ::$ constructorParametersCache [static ::class] = $ constructor
121
+ ? collect ($ constructor ->getParameters ())->map ->getName ()->all ()
122
+ : [];
123
+ }
124
+
125
+ return static ::$ constructorParametersCache [static ::class];
126
+ }
56
127
57
128
/**
58
129
* Resolve the Blade view or view file that should be used when rendering the component.
@@ -72,11 +143,7 @@ public function resolveView()
72
143
}
73
144
74
145
$ resolver = function ($ view ) {
75
- $ factory = Container::getInstance ()->make ('view ' );
76
-
77
- return strlen ($ view ) <= PHP_MAXPATHLEN && $ factory ->exists ($ view )
78
- ? $ view
79
- : $ this ->createBladeViewFromString ($ factory , $ view );
146
+ return $ this ->extractBladeViewFromString ($ view );
80
147
};
81
148
82
149
return $ view instanceof Closure ? function (array $ data = []) use ($ view , $ resolver ) {
@@ -88,13 +155,22 @@ public function resolveView()
88
155
/**
89
156
* Create a Blade view with the raw component string content.
90
157
*
91
- * @param \Illuminate\Contracts\View\Factory $factory
92
158
* @param string $contents
93
159
* @return string
94
160
*/
95
- protected function createBladeViewFromString ( $ factory , $ contents )
161
+ protected function extractBladeViewFromString ( $ contents )
96
162
{
97
- $ factory ->addNamespace (
163
+ $ key = sprintf ('%s::%s ' , static ::class, $ contents );
164
+
165
+ if (isset (static ::$ bladeViewCache [$ key ])) {
166
+ return static ::$ bladeViewCache [$ key ];
167
+ }
168
+
169
+ if (strlen ($ contents ) <= PHP_MAXPATHLEN && $ this ->factory ()->exists ($ contents )) {
170
+ return static ::$ bladeViewCache [$ key ] = $ contents ;
171
+ }
172
+
173
+ $ this ->factory ()->addNamespace (
98
174
'__components ' ,
99
175
$ directory = Container::getInstance ()['config ' ]->get ('view.compiled ' )
100
176
);
@@ -107,7 +183,7 @@ protected function createBladeViewFromString($factory, $contents)
107
183
file_put_contents ($ viewFile , $ contents );
108
184
}
109
185
110
- return '__components:: ' .basename ($ viewFile , '.blade.php ' );
186
+ return static :: $ bladeViewCache [ $ key ] = '__components:: ' .basename ($ viewFile , '.blade.php ' );
111
187
}
112
188
113
189
/**
@@ -292,4 +368,79 @@ public function shouldRender()
292
368
{
293
369
return true ;
294
370
}
371
+
372
+ /**
373
+ * Get the evaluated view contents for the given view.
374
+ *
375
+ * @param string|null $view
376
+ * @param \Illuminate\Contracts\Support\Arrayable|array $data
377
+ * @param array $mergeData
378
+ * @return \Illuminate\Contracts\View\View
379
+ */
380
+ public function view ($ view , $ data = [], $ mergeData = [])
381
+ {
382
+ return $ this ->factory ()->make ($ view , $ data , $ mergeData );
383
+ }
384
+
385
+ /**
386
+ * Get the view factory instance.
387
+ *
388
+ * @return \Illuminate\Contracts\View\Factory
389
+ */
390
+ protected function factory ()
391
+ {
392
+ if (is_null (static ::$ factory )) {
393
+ static ::$ factory = Container::getInstance ()->make ('view ' );
394
+ }
395
+
396
+ return static ::$ factory ;
397
+ }
398
+
399
+ /**
400
+ * Flush the component's cached state.
401
+ *
402
+ * @return void
403
+ */
404
+ public static function flushCache ()
405
+ {
406
+ static ::$ bladeViewCache = [];
407
+ static ::$ constructorParametersCache = [];
408
+ static ::$ methodCache = [];
409
+ static ::$ propertyCache = [];
410
+ }
411
+
412
+ /**
413
+ * Forget the component's factory instance.
414
+ *
415
+ * @return void
416
+ */
417
+ public static function forgetFactory ()
418
+ {
419
+ static ::$ factory = null ;
420
+ }
421
+
422
+ /**
423
+ * Forget the component's resolver callback.
424
+ *
425
+ * @return void
426
+ *
427
+ * @internal
428
+ */
429
+ public static function forgetComponentsResolver ()
430
+ {
431
+ static ::$ componentsResolver = null ;
432
+ }
433
+
434
+ /**
435
+ * Set the callback that should be used to resolve components within views.
436
+ *
437
+ * @param \Closure(string $component, array $data): Component $resolver
438
+ * @return void
439
+ *
440
+ * @internal
441
+ */
442
+ public static function resolveComponentsUsing ($ resolver )
443
+ {
444
+ static ::$ componentsResolver = $ resolver ;
445
+ }
295
446
}
0 commit comments