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.
