CakePHP 3 upload plików
W tej części zajmiemy się implementacją uploadu plików. Utwórzmy w bazie danych tabelę przechowującą pliki. Jest ona powiązana z modelem Applications oraz z Users.
CREATE TABLE `documents` ( `id` bigint(11) NOT NULL, `application_id` bigint(11) NOT NULL, `user_id` bigint(11) NOT NULL, `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, `filename` varchar(125) COLLATE utf8mb4_unicode_ci NOT NULL, `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `modified` datetime NOT NULL, `created` datetime NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ALTER TABLE `documents` ADD PRIMARY KEY (`id`);
namespace App\Model\Entity; use Cake\ORM\Entity; class Document extends Entity { protected $_accessible = [ '*' => true, 'id' => false ]; }Potem src/Model/Table/DocumentsTable.php.
namespace App\Model\Table; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; class DocumentsTable extends Table { public function initialize(array $config) { parent::initialize($config); $this->table('documents'); $this->displayField('name'); $this->primaryKey('id'); $this->addBehavior('Timestamp'); $this->belongsTo('Applications'); $this->belongsTo('Users'); } public function validationDefault(Validator $validator) { $validator ->allowEmpty('id', 'create'); $validator ->requirePresence('name', 'create') ->notEmpty('name'); $validator ->requirePresence('filename', 'create') ->notEmpty('filename'); return $validator; } public function buildRules(RulesChecker $rules) { $rules->add($rules->existsIn(['application_id'], 'Applications')); $rules->add($rules->existsIn(['user_id'], 'Users')); return $rules; } }Nasze dokumenty są powiązane z użytkownikami i aplikacjami więc musimy zaktualizować ich klasy Table src/Model/Table/UsersTable.php oraz src/Model/Table/ApplicationsTable.php. Dodajemy poniższą linię w funkcji initialize.
$this->hasMany('Documents');Podczas ładowania Aplikacji lub Użytkownika musimy koniecznie załączyć powiązane Dokumenty za pomocą contain. W kontrolerach zaktualizuj funkcję view. W pliku src/Controller/ApplicationsController.php:
$application = $this->Applications->get($id, ['contain' => 'Documents']);W pliku src/Controller/UsersController.php:
$user = $this->Users->get($id, ['contain' => 'Documents']);Dodamy dokumenty z poziomu widoku Aplikacji, a także listę powiązanych dokumentów, edytując widok src/Template/Applications/view.ctp i dodając na końcu tego pliku:
<div class="related"> <?php echo $this->Html->link(__('New Document'), ['controller' => 'Documents', 'action' => 'add', $application->id], ['class' => 'right']) ?> <h3><?php echo __('Documents') ?></h3> <?php if (!empty($application->documents)): ?> <table cellpadding="0" cellspacing="0"> <tr> <th scope="col"><?php echo __('Name') ?> <th scope="col"><?php echo __('Description') ?> <th scope="col" class="actions"><?php echo __('Actions') ?> </tr> <?php foreach ($application->documents as $documents): ?> <tr> <td><?php echo $this->Html->link($documents->name, '/files/' . $documents->filename) ?> <td><?php echo h($documents->description) ?></td> <td class="actions"> <?php echo $this->Html->link(__('View'), '/files/' . $documents->filename) ?> | <?php echo $this->Html->link(__('Edit'), ['controller' => 'Documents', 'action' => 'edit', $documents->id]) ?> </td> </tr> <?php endforeach; ?> </table> <?php endif; ?> </div>Dodamy teraz widok dodawania dokumentu (src/Template/Documents/add.ctp).
<div class="documents form large-9 medium-8 columns content"> <?php echo $this->Form->create($document, ['type' => 'file']) ?> <fieldset> <legend><?php echo __('Add Document') ?></legend> <?php echo $this->Form->input('application', ['type' => 'text', 'default' => $application_id, 'disabled' => true]); echo $this->Form->input('name', ['autofocus' => 'true']); echo $this->Form->input('file', ['type' => 'file', 'required' => true]); echo $this->Form->input('description'); echo $this->Form->input('application_id', ['type' => 'hidden', 'value' => $application_id]); echo $this->Form->input('user_id', ['type' => 'hidden', 'value' => $user_id]); ?> </fieldset> <?php echo $this->Form->button(__('Submit')) ?> <?php echo $this->Html->link(__('Cancel'), ['controller' => 'Applications', 'action' => 'view', $application_id], ['class' => 'button']); ?> <?php echo $this->Form->end() ?> </div>Widok edycji dokumentu (src/Template/Documents/edit.ctp).
<div class="documents form large-9 medium-8 columns content"> <?php echo $this->Form->create($document, ['type' => 'file']) ?> <fieldset> <legend><?php echo __('Edit Document') ?></legend> <?php echo $this->Form->input('name'); ?> <div><strong>Current File</strong></div> <?php echo $this->Html->link($document->filename, '/files/' . $document->filename); echo $this->Form->input('file', ['type' => 'file']); echo $this->Form->input('description'); ?> </fieldset> <?php echo $this->Form->button(__('Submit')) ?> <?php echo $this->Html->link(__('Cancel'), ['controller' => 'Applications', 'action' => 'view', $document->application_id], ['class' => 'button']); ?> <?php echo $this->Form->end() ?> </div>Przejdźmy teraz do kontrolera dokumentów. Tworzymy następujący plik (src/Controller/DocumentsController.php).
namespace App\Controller; use App\Controller\AppController; class DocumentsController extends AppController { public function isAuthorized($user) { if ($user['role'] == 'Admin') { // Only Admin can add/edit documents return true; } return false; } public function add($application_id = null) { if (is_null($application_id)) { // Documents must be attached to an application $this->redirect(['controller' => 'Applications', 'action' => 'index']); } $document = $this->Documents->newEntity(); if ($this->request->is('post')) { $file = $this->request->data['file']; $file['name'] = time() . '-' . str_replace(' ', '_', $file['name']); // timestamp files to prevent clobber if (move_uploaded_file($file['tmp_name'], WWW_ROOT . 'files/' . $file['name'])) { $this->request->data['filename'] = $file['name']; $document = $this->Documents->patchEntity($document, $this->request->data); if ($this->Documents->save($document)) { $this->Flash->success(__('The document has been saved.')); return $this->redirect(['controller' => 'Applications', 'action' => 'view', $document->application_id]); } else { $this->Flash->error(__('The document could not be saved. Please, try again.')); } } else { $this->Flash->error(__('Could not upload the file')); } } $user_id = $this->Auth->user('id'); $this->set(compact('document', 'application_id', 'user_id')); $this->set('_serialize', ['document']); } public function edit($id = null) { $document = $this->Documents->get($id); if ($this->request->is(['patch', 'post', 'put'])) { $file = $this->request->data['file']; if ($file['name'] != '' && move_uploaded_file($file['tmp_name'], WWW_ROOT . 'files/' . $file['name'])) { $this->request->data['filename'] = $file['name']; } $document = $this->Documents->patchEntity($document, $this->request->data); if ($this->Documents->save($document)) { $this->Flash->success(__('The document has been saved.')); return $this->redirect(['controller' => 'Applications', 'action' => 'view', $document->application_id]); } else { $this->Flash->error(__('The document could not be saved. Please, try again.')); } } $this->set(compact('document')); $this->set('_serialize', ['document']); } }Ponieważ zapisujemy pliki do katalogu /webroot/files/ naszej aplikacji, upewnij się, że katalog ten istnieje i że użytkownik (np. Apache) ma prawo zapisu do tego katalogu.