1+ <?php
2+
3+ namespace Sg \DatatablesBundle \Maker ;
4+
5+
6+ use Symfony \Bundle \MakerBundle \InputAwareMakerInterface ;
7+ use Symfony \Bundle \MakerBundle \Maker \AbstractMaker ;
8+ use Symfony \Bundle \MakerBundle \ConsoleStyle ;
9+ use Symfony \Bundle \MakerBundle \MakerInterface ;
10+ use Symfony \Component \Console \Command \Command ;
11+ use Symfony \Component \Console \Input \InputInterface ;
12+ use Symfony \Bundle \MakerBundle \InputConfiguration ;
13+ use Symfony \Bundle \MakerBundle \DependencyBuilder ;
14+ Use Symfony \Bundle \MakerBundle \Generator ;
15+ use Symfony \Bundle \MakerBundle \Doctrine \DoctrineHelper ;
16+ use Symfony \Component \Console \Input \InputArgument ;
17+ use Symfony \Bundle \MakerBundle \Str ;
18+ use Symfony \Bundle \MakerBundle \Validator ;
19+ use Symfony \Bundle \TwigBundle ;
20+ use Doctrine \Bundle \DoctrineBundle ;
21+ use Sensio \Bundle \FrameworkExtraBundle \Configuration \ParamConverter ;
22+ use Symfony \Component \Console \Question \Question ;
23+ use Doctrine \ORM \Mapping \ClassMetadataInfo ;
24+
25+
26+ class MakeDatatable extends AbstractMaker
27+ {
28+ private $ doctrineHelper ;
29+ private $ baseSleleton ;
30+ private $ columnTypes = [ 'ActionColumn ' => 'ActionColumn ' ];
31+
32+ public function __construct (DoctrineHelper $ doctrineHelper )
33+ {
34+ $ this ->doctrineHelper = $ doctrineHelper ;
35+ $ this ->baseSleleton = realpath (__DIR__ .'/../Resources/views/skeleton ' );
36+ }
37+
38+
39+ public static function getCommandName (): string
40+ {
41+ return 'make:datatable ' ;
42+ }
43+
44+ /**
45+ * {@inheritdoc}
46+ */
47+ public function configureCommand (Command $ command , InputConfiguration $ inputConfig )
48+ {
49+ $ command
50+ ->setDescription ('Creates Datable for Doctrine entity class ' )
51+ ->addArgument ('entity-class ' , InputArgument::OPTIONAL , sprintf ('The class name of the entity to create CRUD (e.g. <fg=yellow>%s</>) ' , Str::asClassName (Str::getRandomTerm ())))
52+ ->setHelp (file_get_contents (__DIR__ .'/../Resources/help/MakeDatatable.txt ' ))
53+ ;
54+ $ inputConfig ->setArgumentAsNonInteractive ('entity-class ' );
55+ }
56+
57+ public function interact (InputInterface $ input , ConsoleStyle $ io , Command $ command )
58+ {
59+ if (null === $ input ->getArgument ('entity-class ' )) {
60+ $ argument = $ command ->getDefinition ()->getArgument ('entity-class ' );
61+ $ entities = $ this ->doctrineHelper ->getEntitiesForAutocomplete ();
62+ $ question = new Question ($ argument ->getDescription ());
63+ $ question ->setAutocompleterValues ($ entities );
64+ $ value = $ io ->askQuestion ($ question );
65+ $ input ->setArgument ('entity-class ' , $ value );
66+ }
67+ }
68+
69+ /**
70+ * {@inheritdoc}
71+ */
72+ public function configureDependencies (DependencyBuilder $ dependencies )
73+ {
74+ }
75+
76+ public function generate (InputInterface $ input , ConsoleStyle $ io , Generator $ generator )
77+ {
78+ $ entityClassDetails = $ generator ->createClassNameDetails (
79+ Validator::entityExists ($ input ->getArgument ('entity-class ' ), $ this ->doctrineHelper ->getEntitiesForAutocomplete ()),
80+ 'Entity \\'
81+ );
82+
83+
84+ $ entityDoctrineDetails = $ this ->doctrineHelper ->createDoctrineDetails ($ entityClassDetails ->getFullName ());
85+
86+ $ datatableClassDetails = $ generator ->createClassNameDetails (
87+ $ entityClassDetails ->getRelativeNameWithoutSuffix (),
88+ 'Datatables \\' ,
89+ 'Datatable '
90+ );
91+
92+ $ className = $ datatableClassDetails ->getShortName ();
93+ $ entityClassLowerCase = strtolower ($ className );
94+
95+ $ metadata = $ this ->getEntityMetadata ($ entityClassDetails ->getFullName ());
96+ $ fields = $ this ->getFieldsFromMetadata ($ metadata );
97+
98+ sort ($ this ->columnTypes );
99+ $ generator ->generateClass (
100+ $ datatableClassDetails ->getFullName (),
101+ $ this ->baseSleleton . '/Datatable.tpl.php ' ,
102+ [
103+ 'bounded_full_class_name ' => $ entityClassDetails ->getFullName (),
104+ 'bounded_class_name ' => $ entityClassDetails ->getShortName (),
105+ 'fields ' => $ fields ,
106+ 'class_name ' => $ className ,
107+ 'datatable_name ' => $ entityClassLowerCase .'_datatable ' ,
108+ 'route_pref ' => $ entityClassLowerCase ,
109+ 'column_types ' => $ this ->columnTypes ,
110+ 'id ' => $ this ->getIdentifierFromMetadata ($ metadata ),
111+ ]
112+ );
113+
114+ $ generator ->writeChanges ();
115+
116+ }
117+
118+ //-------------------------------------------------
119+ // Helper
120+ //-------------------------------------------------
121+
122+ /**
123+ * Parse fields.
124+ *
125+ * @param string $input
126+ *
127+ * @return array
128+ */
129+ private static function parseFields ($ input )
130+ {
131+ $ fields = array ();
132+
133+ foreach (explode (' ' , $ input ) as $ value ) {
134+ $ elements = explode (': ' , $ value );
135+
136+ $ row = array ();
137+ $ row ['property ' ] = $ elements [0 ];
138+ $ row ['column_type ' ] = 'Column::class ' ;
139+ $ row ['data ' ] = null ;
140+ $ row ['title ' ] = ucwords (str_replace ('. ' , ' ' , $ elements [0 ]));
141+
142+ if (isset ($ elements [1 ])) {
143+ switch ($ elements [1 ]) {
144+ case 'datetime ' :
145+ $ row ['column_type ' ] = 'DateTimeColumn::class ' ;
146+ break ;
147+ case 'boolean ' :
148+ $ row ['column_type ' ] = 'BooleanColumn::class ' ;
149+ break ;
150+ }
151+ }
152+
153+ $ fields [] = $ row ;
154+ }
155+
156+ return $ fields ;
157+ }
158+
159+ private function getEntityMetadata ($ class )
160+ {
161+ return $ this ->doctrineHelper ->getMetadata ($ class , true );
162+ }
163+
164+ /**
165+ * Get Id from metadata.
166+ *
167+ * @param array $metadata
168+ *
169+ * @return mixed
170+ * @throws Exception
171+ */
172+ private function getIdentifierFromMetadata (ClassMetadataInfo $ metadata )
173+ {
174+ if (count ($ metadata ->identifier ) > 1 ) {
175+ throw new Exception ('CreateDatatableCommand::getIdentifierFromMetadata(): The Datatable generator does not support entities with multiple primary keys. ' );
176+ }
177+
178+ return $ metadata ->identifier ;
179+ }
180+
181+ /**
182+ * Returns an array of fields. Fields can be both column fields and
183+ * association fields.
184+ *
185+ * @param ClassMetadataInfo $metadata
186+ *
187+ * @return array $fields
188+ */
189+ private function getFieldsFromMetadata (ClassMetadataInfo $ metadata )
190+ {
191+ $ fields = array ();
192+
193+ foreach ($ metadata ->fieldMappings as $ field ) {
194+ $ row = array ();
195+ $ row ['property ' ] = $ field ['fieldName ' ];
196+
197+ switch ($ field ['type ' ]) {
198+ case 'datetime ' :
199+ $ row ['column_type ' ] = 'DateTimeColumn::class ' ;
200+ break ;
201+ case 'boolean ' :
202+ $ row ['column_type ' ] = 'BooleanColumn::class ' ;
203+ break ;
204+ default :
205+ $ row ['column_type ' ] = 'Column::class ' ;
206+ }
207+
208+ $ row ['title ' ] = ucwords ($ field ['fieldName ' ]);
209+ $ row ['data ' ] = null ;
210+ $ fields [] = $ row ;
211+ if (!isset ($ this ->columnTypes [$ row ['column_type ' ]])) {
212+ $ this ->columnTypes [$ row ['column_type ' ]] = substr ($ row ['column_type ' ], 0 , -7 );
213+ }
214+
215+ }
216+
217+ foreach ($ metadata ->associationMappings as $ relation ) {
218+ $ targetClass = $ relation ['targetEntity ' ];
219+ $ targetMetadata = $ this ->getEntityMetadata ($ targetClass , true );
220+
221+ foreach ($ targetMetadata ->fieldMappings as $ field ) {
222+ $ row = array ();
223+ $ row ['property ' ] = $ relation ['fieldName ' ].'. ' .$ field ['fieldName ' ];
224+ $ row ['column_type ' ] = 'Column::class ' ;
225+ $ row ['title ' ] = ucwords (str_replace ('. ' , ' ' , $ row ['property ' ]));
226+ if ($ relation ['type ' ] === ClassMetadataInfo::ONE_TO_MANY || $ relation ['type ' ] === ClassMetadataInfo::MANY_TO_MANY ) {
227+ $ row ['data ' ] = $ relation ['fieldName ' ].'[, ]. ' .$ field ['fieldName ' ];
228+ } else {
229+ $ row ['data ' ] = null ;
230+ }
231+ $ fields [] = $ row ;
232+ }
233+ }
234+
235+ return $ fields ;
236+ }
237+ }
0 commit comments