<?php
/**
* ImageBehavior - take best from database blobs adn file image storage
* requires 'content' field that is a blob (mediumblob or longblob), and
* 'ext' varchar(10) field and
* 'modified' datetime field
* @author Grzegorz Pawlik
* @version 1.0
*/
class ImageBehavior extends ModelBehavior {
/**
* directory in which cached files will be stored
*
* @var string
*/
var $cacheSubdir = 'filecache';
/**
* if set to false - never check if cached file is present (nor actual)
*
* @var bool
*/
var $usecache = true;
function setup(&$Model) {
// no setup at this time
}
/**
* Insert proper blob when standard data after upload is present
*
* @param object $Model
* @return bool true
*/
function beforeSave(&$Model) {
if(isset($Model->data[$Model->name]['file']['tmp_name']) && is_uploaded_file($Model->data[$Model->name]['file']['tmp_name'])) {
// podnieś wyżej parametry
$Model->data[$Model->name] = array_merge($Model->data[$Model->name], $Model->data[$Model->name]['file']);
// przygotuj blob
$this->_prepareBlob($Model);
$this->_getExt($Model);
}
return true;
}
/**
* prepares blob contents
*
* @param object $Model
*/
function _prepareBlob(&$Model) {
App::import('Core', 'File');
$file = new File($Model->data['Medium']['tmp_name'], false);
$content = $this->addSlashes( $file->read() );
$Model->data[$Model->name]['content'] = $content;
}
/**
* Get uploaded file extension
*
* @param object $Model
*/
function _getExt(&$Model) {
$file = explode('.', $Model->data['Medium']['name']);
$ext = array_pop($file);
$Model->data[$Model->name]['ext'] = $ext;
}
/**
* replace blob contents with file path
* After reading database checks if cached file is present. If not creates it (from blob contents) and
* returns a 'file' field with path relative to /app/webroot/img
*
*
* @param object $model
* @param array $results
* @param unknown_type $primary
* @return unknown
*/
function afterFind(&$model, $results, $primary) {
foreach($results as $key => $val) {
$relpath = $this->cacheSubdir . DS .
$val[$model->name]['id'] . '_' . $model->name . '_' .
$val[$model->name]['modified'] . '.' . $val[$model->name]['ext'];
$relpath = str_replace( array(' ', ':') , '_', $relpath);
$fullpath = IMAGES . $relpath;
if(!file_exists($fullpath) || !$this->usecache ) {
file_put_contents($fullpath, $this->stripSlashes($results[$key][$model->name]['content']));
}
$results[$key][$model->name]['file'] = $relpath;
// remove blob from results (its messy when You want to output results in debug)
unset($results[$key][$model->name]['content']);
}
return $results;
}
/**
* add slashes (just wrapper)
*
* @param string $string
* @return string with slashes
*/
function addSlashes($string) {
return addslashes($string);
}
/**
* strip slashes (just wrapper)
*
* @param string $string
* @return string without slashes
*/
function stripSlashes($string) {
return stripslashes($string);
}
}
?>
Zasada działania jest dość prosta. Wyjaśnię ją na przykładzie.
Tabela media:
CREATE TABLE IF NOT EXISTS `media` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
`ext` varchar(10) NOT NULL,
`content` longblob NOT NULL,
`size` int(11) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`type` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
Model:
<?php
class Medium extends AppModel {
var $name = 'Medium';
var $actsAs = array('Image');
}
?>
Kontroler:
<?php
class MediaController extends AppController {
var $name = 'Media';
var $helpers = array('Html', 'Form');
function index() {
$this->set('media', $this->Medium->findAll());
}
function add() {
if(!empty($this->data)) {
$this->Medium->save($this->data);
}
}
}
?>
Przy uploadzie ImageBehavior oczekuje, że plik będzie przekazany w poly ModelName.file (tutaj Media.file).
add.ctp:
span style="color: rgb(0, 0, 187);"><?php
echo $form->create(
array('url' => array(
'controller' => 'media',
'action' => 'add'
),
'enctype' => 'multipart/form-data'
)
);
?>
<?php echo $form->file('Medium.file'); ?>
<?php echo $form->end('submit'); ?>
Przy odczycie dzieje się to co mnie najbardziej interesowało. Zamiast dostać zawartość (BLOB) pliku, dostajemy w polu file ścieżkę (relatywną do app/webroot/img). Domyślne ustawienia wymagają, żeby był tam katalog filecache (z możliwością zapisu). Przy operacji read behavior sprawdzi, czy istnieje aktualy plik w filecache, i jesli nie - utworzy go.
index.ctp:
<?php foreach($media as $medium): ?>
<?php echo $html->image($medium['Medium']['file']); ?>
<?php endforeach; ?>
To rozwiązanie ma przynajmniej dwa zauważalne braki:
- Gdy dodamy taki plik do treści np. postu, po jego updacie - nie będą widoczne zmiany
- Dobrze byłoby, gdyby przy operacji read nie zwracał zawartości BLOB (ważne, gdy baza jest gdzieś dalej), ale odpytywał tylko wtedy, gdy jest potrzebne zaktualizowanie zawartości pliku w filecache.
Brak komentarzy:
Prześlij komentarz