33
44namespace Arkitect \RuleBuilders \Architecture ;
55
6+ use Arkitect \Expression \ForClasses \DependsOnlyOnTheseNamespaces ;
67use Arkitect \Expression \ForClasses \NotDependsOnTheseNamespaces ;
78use Arkitect \Expression \ForClasses \ResideInOneOfTheseNamespaces ;
89use Arkitect \Rules \Rule ;
910
10- class Architecture implements Component, DefinedBy, Where, MayDependOnComponents, MayDependOnAnyComponent, ShouldNotDependOnAnyComponent, Rules
11+ class Architecture implements Component, DefinedBy, Where, MayDependOnComponents, MayDependOnAnyComponent, ShouldNotDependOnAnyComponent, ShouldOnlyDependOnComponents, Rules
1112{
1213 /** @var string */
1314 private $ componentName ;
1415 /** @var array<string, string> */
1516 private $ componentSelectors ;
1617 /** @var array<string, string[]> */
1718 private $ allowedDependencies ;
19+ /** @var array<string, string[]> */
20+ private $ componentDependsOnlyOnTheseNamespaces ;
1821
1922 private function __construct ()
2023 {
2124 $ this ->componentName = '' ;
2225 $ this ->componentSelectors = [];
2326 $ this ->allowedDependencies = [];
27+ $ this ->componentDependsOnlyOnTheseNamespaces = [];
2428 }
2529
2630 public static function withComponents (): Component
@@ -38,7 +42,6 @@ public function component(string $name): DefinedBy
3842 public function definedBy (string $ selector )
3943 {
4044 $ this ->componentSelectors [$ this ->componentName ] = $ selector ;
41- $ this ->allowedDependencies [$ this ->componentName ] = [];
4245
4346 return $ this ;
4447 }
@@ -57,6 +60,13 @@ public function shouldNotDependOnAnyComponent()
5760 return $ this ;
5861 }
5962
63+ public function shouldOnlyDependOnComponents (string ...$ componentNames )
64+ {
65+ $ this ->componentDependsOnlyOnTheseNamespaces [$ this ->componentName ] = $ componentNames ;
66+
67+ return $ this ;
68+ }
69+
6070 public function mayDependOnComponents (string ...$ componentNames )
6171 {
6272 $ this ->allowedDependencies [$ this ->componentName ] = $ componentNames ;
@@ -76,19 +86,32 @@ public function rules(): iterable
7686 $ layerNames = array_keys ($ this ->componentSelectors );
7787
7888 foreach ($ this ->componentSelectors as $ name => $ selector ) {
79- $ forbiddenComponents = array_diff ($ layerNames , [$ name ], $ this ->allowedDependencies [$ name ]);
89+ if (isset ($ this ->allowedDependencies [$ name ])) {
90+ $ forbiddenComponents = array_diff ($ layerNames , [$ name ], $ this ->allowedDependencies [$ name ]);
91+
92+ if (!empty ($ forbiddenComponents )) {
93+ $ forbiddenSelectors = array_map (function (string $ componentName ): string {
94+ return $ this ->componentSelectors [$ componentName ];
95+ }, $ forbiddenComponents );
96+
97+ yield Rule::allClasses ()
98+ ->that (new ResideInOneOfTheseNamespaces ($ selector ))
99+ ->should (new NotDependsOnTheseNamespaces (...$ forbiddenSelectors ))
100+ ->because ('of component architecture ' );
101+ }
102+ }
80103
81- if (empty ( $ forbiddenComponents )) {
104+ if (! isset ( $ this -> componentDependsOnlyOnTheseNamespaces [ $ name ] )) {
82105 continue ;
83106 }
84107
85- $ forbiddenSelectors = array_map (function (string $ componentName ): string {
108+ $ allowedDependencies = array_map (function (string $ componentName ): string {
86109 return $ this ->componentSelectors [$ componentName ];
87- }, $ forbiddenComponents );
110+ }, $ this -> componentDependsOnlyOnTheseNamespaces [ $ name ] );
88111
89112 yield Rule::allClasses ()
90113 ->that (new ResideInOneOfTheseNamespaces ($ selector ))
91- ->should (new NotDependsOnTheseNamespaces (...$ forbiddenSelectors ))
114+ ->should (new DependsOnlyOnTheseNamespaces (...$ allowedDependencies ))
92115 ->because ('of component architecture ' );
93116 }
94117 }
0 commit comments