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`);
Potem src/Model/Table/DocumentsTable.php.
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class Document extends Entity
- {
- protected $_accessible = [
- '*' => true,
- 'id' => false
- ];
- }
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.
- 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;
- }
- }
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:
- $this->hasMany('Documents');
W pliku src/Controller/UsersController.php:
- $application = $this->Applications->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:
- $user = $this->Users->get($id, ['contain' => 'Documents']);
Dodamy teraz widok dodawania dokumentu (src/Template/Documents/add.ctp).
- <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>
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 __('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>
Przejdźmy teraz do kontrolera dokumentów. Tworzymy następujący plik (src/Controller/DocumentsController.php).
- <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>
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.
- 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']);
- }
- }