88use ApiPlatform \Metadata \Operation ;
99use ApiPlatform \State \ProcessorInterface ;
1010use App \Dto \ScreenInput ;
11- use App \Entity \Tenant \Playlist ;
1211use App \Entity \Tenant \PlaylistScreenRegion ;
1312use App \Entity \Tenant \Screen ;
1413use App \Repository \PlaylistRepository ;
@@ -32,7 +31,7 @@ public function __construct(
3231 EntityManagerInterface $ entityManager ,
3332 ProcessorInterface $ persistProcessor ,
3433 ProcessorInterface $ removeProcessor ,
35- ScreenProvider $ provider
34+ ScreenProvider $ provider,
3635 ) {
3736 parent ::__construct ($ entityManager , $ persistProcessor , $ removeProcessor , $ provider );
3837 }
@@ -42,6 +41,10 @@ protected function fromInput(mixed $object, Operation $operation, array $uriVari
4241 // FIXME Do we really have to do (something like) this to load an existing object into the entity manager?
4342 $ screen = $ this ->loadPrevious (new Screen (), $ context );
4443
44+ if (!$ screen instanceof Screen) {
45+ throw new InvalidArgumentException ('object must be of type Screen ' );
46+ }
47+
4548 assert ($ object instanceof ScreenInput);
4649 empty ($ object ->title ) ?: $ screen ->setTitle ($ object ->title );
4750 empty ($ object ->description ) ?: $ screen ->setDescription ($ object ->description );
@@ -56,57 +59,67 @@ protected function fromInput(mixed $object, Operation $operation, array $uriVari
5659 $ screen ->setEnableColorSchemeChange ($ object ->enableColorSchemeChange );
5760 }
5861
59- // Adding relations for playlist/screen/region
60- if (isset ($ object ->regions ) && isset ($ screen )) {
62+ // Adding relations for playlist/screen/region if object has region property.
63+ if (isset ($ object ->regions )) {
64+ // Ensure regions object has valid structure
65+ $ this ->validateRegionsAndPlaylists ($ object ->regions );
66+
67+ $ existingPlaylistScreenRegions = $ screen ->getPlaylistScreenRegions ();
68+
6169 foreach ($ object ->regions as $ regionAndPlaylists ) {
62- // Relevant region
63- $ region = $ this ->screenLayoutRegionsRepository ->findOneBy (['id ' => $ regionAndPlaylists ['regionId ' ]]);
70+ $ regionId = $ regionAndPlaylists ['regionId ' ];
71+
72+ $ region = $ this ->screenLayoutRegionsRepository ->findOneBy (['id ' => $ regionId ]);
6473
6574 if (is_null ($ region )) {
66- throw new InvalidArgumentException ('Unknown region resource ' );
75+ throw new InvalidArgumentException (sprintf ( 'Unknown region resource (id: %s) ' , $ regionId ) );
6776 }
6877
69- // Collection to be saved.
70- $ playlistScreenRegionCollection = new ArrayCollection ();
78+ $ existingPlaylistScreenRegionsInRegion = $ existingPlaylistScreenRegions ->filter (
79+ fn (PlaylistScreenRegion $ psr ) => $ psr ->getRegion ()?->getId() == $ regionId
80+ );
81+
82+ $ inputPlaylists = $ regionAndPlaylists ['playlists ' ];
83+ $ inputPlaylistIds = array_map (fn (array $ entry ): string => $ entry ['id ' ], $ inputPlaylists );
84+
85+ // Remove playlist screen regions that should not exist in region.
86+ /** @var PlaylistScreenRegion $existingPSR */
87+ foreach ($ existingPlaylistScreenRegionsInRegion as $ existingPSR ) {
88+ if (!in_array ($ existingPSR ->getPlaylist ()?->getId(), $ inputPlaylistIds )) {
89+ $ screen ->removePlaylistScreenRegion ($ existingPSR );
90+ }
91+ }
7192
72- // Looping through playlists connected to region
73- foreach ($ regionAndPlaylists ['playlists ' ] as $ inputPlaylist ) {
74- // Checking if playlists exists
93+ // Add or update the input playlists.
94+ foreach ($ inputPlaylists as $ inputPlaylist ) {
7595 $ playlist = $ this ->playlistRepository ->findOneBy (['id ' => $ inputPlaylist ['id ' ]]);
7696
7797 if (is_null ($ playlist )) {
78- throw new InvalidArgumentException ('Unknown playlist resource ' );
98+ throw new InvalidArgumentException (sprintf ( 'Unknown playlist resource (id: %s) ' , $ inputPlaylist [ ' id ' ]) );
7999 }
80100
81- // See if relation already exists
82- $ existingPlaylistScreenRegion = $ this ->playlistScreenRegionRepository ->findOneBy ([
83- 'screen ' => $ screen ,
84- 'region ' => $ region ,
101+ $ existingPlaylistScreenRegionRelation = $ this ->playlistScreenRegionRepository ->findOneBy ([
85102 'playlist ' => $ playlist ,
103+ 'region ' => $ region ,
104+ 'screen ' => $ screen ,
86105 ]);
87106
88- if (is_null ($ existingPlaylistScreenRegion )) {
89- // If relation does not exist, create new PlaylistScreenRegion
107+ if (!is_null ($ existingPlaylistScreenRegionRelation )) {
108+ $ existingPlaylistScreenRegionRelation ->setWeight ($ inputPlaylist ['weight ' ] ?? 0 );
109+ } else {
90110 $ newPlaylistScreenRegionRelation = new PlaylistScreenRegion ();
91111 $ newPlaylistScreenRegionRelation ->setPlaylist ($ playlist );
92112 $ newPlaylistScreenRegionRelation ->setRegion ($ region );
93113 $ newPlaylistScreenRegionRelation ->setScreen ($ screen );
94114 $ newPlaylistScreenRegionRelation ->setWeight ($ inputPlaylist ['weight ' ] ?? 0 );
95- /** @psalm-suppress InvalidArgument */
96- $ playlistScreenRegionCollection ->add ($ newPlaylistScreenRegionRelation );
97- } else {
98- // Update weight, add existing relation
99- $ existingPlaylistScreenRegion ->setWeight ($ inputPlaylist ['weight ' ] ?? 0 );
100- /** @psalm-suppress InvalidArgument */
101- $ playlistScreenRegionCollection ->add ($ existingPlaylistScreenRegion );
115+ $ screen ->addPlaylistScreenRegion ($ newPlaylistScreenRegionRelation );
102116 }
103117 }
104- $ region ->setPlaylistScreenRegions ($ playlistScreenRegionCollection );
105118 }
106119 }
107120
108121 // Maps ids of existing groups
109- if (isset ($ object ->groups ) && isset ( $ screen ) ) {
122+ if (isset ($ object ->groups )) {
110123 $ groupCollection = new ArrayCollection ();
111124 foreach ($ object ->groups as $ group ) {
112125 $ groupToSave = $ this ->groupRepository ->findOneBy (['id ' => $ group ]);
@@ -134,4 +147,38 @@ protected function fromInput(mixed $object, Operation $operation, array $uriVari
134147
135148 return $ screen ;
136149 }
150+
151+ private function validateRegionsAndPlaylists (array $ regions ): void
152+ {
153+ foreach ($ regions as $ region ) {
154+ $ this ->validateRegion ($ region );
155+
156+ foreach ($ region ['playlists ' ] as $ playlist ) {
157+ $ this ->validatePlaylist ($ playlist );
158+ }
159+ }
160+ }
161+
162+ private function validateRegion (array $ region ): void
163+ {
164+ if (!isset ($ region ['regionId ' ]) || !is_string ($ region ['regionId ' ])) {
165+ throw new InvalidArgumentException ('All regions must specify a valid Ulid ' );
166+ }
167+
168+ if (!isset ($ region ['playlists ' ]) || !is_array ($ region ['playlists ' ])) {
169+ throw new InvalidArgumentException ('All regions must specify a list of playlists ' );
170+ }
171+ }
172+
173+ private function validatePlaylist (array $ playlist ): void
174+ {
175+ if (!isset ($ playlist ['id ' ]) || !is_string ($ playlist ['id ' ])) {
176+ throw new InvalidArgumentException ('All playlists must specify a valid Ulid ' );
177+
178+ }
179+
180+ if (isset ($ playlist ['weight ' ]) && !is_integer ($ playlist ['weight ' ])) {
181+ throw new InvalidArgumentException ('Playlists weight must be an integer ' );
182+ }
183+ }
137184}
0 commit comments