Model Callback beforeDelete - cakephp

I'm trying to delete images when deleting the container of those images with a cascading model::delete
The cascading works fine, but I can't get the model call back afterDelete to work properly so I can delete the actual image files when doing the delete.
function beforeDelete() {
$containerId = $this->id;
$numberOfImages = $this->RelatedImage->find('count', array('conditions' => array('RelatedImage.container_id' => 'containerId')));
if ($numberOfImages > 0){
$relatedImages = $this->RelatedImage->find('all', array('conditions' => array('RelatedImage.container_id' => 'containerId')));
foreach ($relatedImages as $image) {
$myFile = WWW_ROOT . 'image' . $containerId . '_i' . $image['RelatedImage']['id'] . '.jpg';
unlink($myFile);
$myThumb = WWW_ROOT . 'img/' . $image['RelatedImage']['thumbnail'];
unlink($myThumb);
}
return true;
} else{
return false;
}
}
The if statement fails each time, even though I know there are images in the table. If I can get the if statement to at least execute i will add further validation on the unlink.

I would do it in this way:
in beforeDelete get the images data
function beforeDelete(){
$relatedImages = $this->RelatedImage->find('all', array('conditions' => array('RelatedImage.container_id' => 'containerId')));
$this->relatedImages = $relatedImages;
$this->currentId = $this->id; //I am not sure if this is necessary
return true;
}
then in the afterDelete() as Oscar suggest do the actual delete of the image:
function afterDelete(){
$relatedImages = $this->relatedImages;
$containerId = $this->currentId; //probably this could be just $this->id;
foreach ($relatedImages as $image) {
$myFile = WWW_ROOT . 'image' . $containerId . '_i' . $image['RelatedImage']['id'] . '.jpg';
unlink($myFile);
$myThumb = WWW_ROOT . 'img/' . $image['RelatedImage']['thumbnail'];
unlink($myThumb);
}
}
this way you are save, even if the model fail to delete the record you will delete images only if the delete was actually happen.
HTH

If the models are related with hasMany/hasOne, both RelatedImage::beforeDelete() and RelatedImage::afterDelete() should be called when they are removed. Try putting the delete logic there instead?

Related

JoomGallery.net | image ordering asc

i would like to use this simple image slider for the joomgallery for my art students:
https://github.com/danielhpavey/joomgallery-slider
the only problem is the ordering. how do i get an ascending image ordering by id and not by filename ?
thanks peter
<?php
class images
{
public function __construct()
{
$file = JPATH_ROOT. '/components/com_joomgallery/interface.php';
if(!file_exists($file)){
JError::raiseError(500, 'JoomGallery seems not to be installed');
} else {
require_once $file;
$this ->interface = new JoomInterface();
}
}
public function getFirstImage()
{
$images = $this ->talkToJoomgallery();
return $images[0];
}
public function getImages()
{
$images = $this ->talkToJoomgallery();
return $images;
}
public function talkToJoomgallery()
{
$images = $this ->interface ->getPicsByCategory( $this ->categoryid );
$imagepath = $this ->joomgalleryImagePath();
$theimages = array();
$c = 0;
foreach ($images as $i){
$theimages[$c]= array(
'imgpath' => JURI::base() . $imagepath . $i->catpath . '/' . $i->imgfilename
,'imgtitle' => $i->imgtitle
,'imgtext' => $i->imgtext
);
$c ++;
}
shuffle($theimages);
return $theimages;
}
private function joomgalleryImagePath()
{
return $this ->interface ->getJConfig( 'jg_pathoriginalimages' );
}
public function __set($property, $value){
$this->$property = $value;
}
}
Your images are loaded according to image id only but this command is reindexing the array i.e shuffle($theimages);
You can comment out that line by
//shuffle($theimages)
Also for ordering images you can change this line in helper.php file
$images = $this->interface->getPicsByCategory($this->categoryid);
to
$images = $this->interface->getPicsByCategory($this->categoryid,null,'ordering' );
This will do the ordering of images the way you did drag and drop of images at the joomla administrator backend.
UPDATE AS PER YOUR LATEST QUERY
Suppose you want to add a param value (sorting) that can be controlled through admin. You ned to change the xml file mod_joomgallery_slider.xml
Just add a new field like this
<field
name = "sorting"
type = "radio"
label = "Sorting"
description = "Sort by Ordering or random"
default = "ordering"
>
<option value = "ordering">Ordering</option>
<option value = "rand()">Random</option>
</field>
Next to get the param in helper.php file then change the function talkToJoomgallery() like this
public function talkToJoomgallery()
{
//Externally calling a module param
jimport( 'joomla.html.parameter' );
jimport( 'joomla.application.module.helper' );
$module = JModuleHelper::getModule('mod_joomgallery_slider');
$moduleParams = new JRegistry();
$moduleParams->loadString($module->params);
$sorting = $moduleParams->get( 'sorting' );
$images = $this ->interface ->getPicsByCategory( $this ->categoryid,null,$sorting );
$imagepath = $this ->joomgalleryImagePath();
$theimages = array();
$c = 0;
foreach ($images as $i){
$theimages[$c]= array(
'imgpath' => JURI::base() . $imagepath . $i->catpath . '/' . $i->imgfilename
,'imgtitle' => $i->imgtitle
,'imgtext' => $i->imgtext
);
$c ++;
}
//var_dump($theimages); exit;
//shuffle($theimages);
return $theimages;
}
UPDATE: To display 2 slider modules on a single page.
Some files needs to changed:
In mod_joomgallery_slider.php file change this line at top
include('helper.php');
To
include_once('helper.php');
This ensures that the file is included once.
Another change will be to remove the function imageText in default.php and including it in helper.php class else a function redeclaration error will be thrown. But now the default.php file still give error, as function imageText will be not defined, but you already added that function to helper.php. So default.php will only work if you change
echo imageText( $i, $params );
To
echo $image->imageText( $i, $params );// You are calling helper object
Remember to change in both if and else conditions.

Find all in CakePHP is dumping a semicolon in my view

I don't know what exactly is happening with my CakePHP app. It worked last week and I have literally changed nothing with that particular file.
When I use find 'all' in my controller it dumps a semi-colon in my view even if there is nothing in the view file.
Here is my code
$evts = $this->Event->find('all');
There is nothing in my view file. I don't know if it makes a difference but I'm using a json view.
As requested here is the complete code for the action
public function search(){
$this->Event->recursive = 2;
$conditions = array();
if(!empty($this->request->query['name'])){
$conditions = array('Event.name LIKE ' => "%" . str_replace(" ","%", $this->request->query['name']) . "%");
}
if(!empty($this->request->query['home'])){
$conditions = array('Event.home_team_id' => $this->request->query['home']);
}
if(!empty($this->request->query['away'])){
$conditions = array('Event.away_team_id' => $this->request->query['away']);
}
$limit = 25;
if(!empty($this->request->query['limit'])){
$limit = $this->request->query['limit'];
}
//$evts = $this->Event->find('all',array('conditions'=>array($conditions),'order' => array('Event.start_time'),'limit'=>$limit));
$evts = $this->Event->find('all');
$this->set('events',$evts);
}
Everything in the view has been commented out ... but here is the code anyway
$results = array();
$i = 0;
foreach ($events as $event) {
$results[$i]['id'] = $event['Event']['id'];
$results[$i]['label'] = $event['Event']['name'] . "(" . date_format(date_create($event['Event']['start_time']), 'D, jS M Y') . ")";
$results[$i]['value'] = $event['Event']['name'];
$results[$i]['home_team_name'] = $event['HomeTeam']['name'];
$results[$i]['away_team_name'] = $event['AwayTeam']['name'];
$results[$i]['sport_name'] = $event['Sport']['name'];
$results[$i]['tournament_name'] = $event['Tournament']['name'];
$results[$i]['start_time'] = $event['Event']['start_time'];
$results[$i]['img'] = $event['Event']['img_path'];
$results[$i]['listener_count'] = 0; //TODO Get the follower count
$i++;
}
echo json_encode($results);
Display
If you changed nothing with that particular file then perhaps you have installed callbacks (either in the controller or in the model) that echo that ";". Look for 'afterFind' calls...
Search your model and your controller folders for "echo statement". In fact you should search your entire app folder except for the Views folder.

upload a file in cakePHP

Hi I want to upload a file using cake php, i got the file and i am using
move_uploaded_file() to move to a specific location but it is not moving my simple logic is
shown below
if (move_uploaded_file($this->data['Add']['upload']['tmp_name'], APP . 'views' . DS .
'static' . DS.'uploads'.DS.'Rajaram'.DS )) {
LogUtil::$logger->debug('KMP File upload Url :
'.var_export($this->data, true));
}
Thanks in Advance.
File uploading is something CakePHP doesn’t do out of the box, which is one of the only thing that annoys me about the framework.
I tackled this by adding file handling to a model using callback methods. I upload the actual file with beforeSave(), and delete the file from the file system with beforeDelete(). A sample model looks like this:
<?php
App::uses('File', 'Utility');
class Image extends AppModel {
public $name = 'Image';
public function beforeSave($options = array()) {
$fieldName = 'filename';
$field = $this->data[$this->alias][$fieldName];
if (!is_array($field)) {
$this->validationErrors[$fieldName][] = 'No file detected';
return false;
}
switch ($field['error']) {
case UPLOAD_ERR_OK:
$newFilename = time() . '.jpg';
$uploadDir = WWW_ROOT . 'files/';
$source = $field['tmp_name'];
$destination = $uploadDir . $newFilename;
if (move_uploaded_file($source, $destination)) {
$this->data[$this->alias][$fieldName] = $newFilename;
return true;
}
else {
$this->validationErrors[$fieldName][] = 'No file detected';
return false;
}
break;
default:
$this->validationErrors[$fieldName][] = 'No file detected';
return false;
break;
}
}
public function beforeDelete($cascade = true) {
$image = $this->findById($this->id);
$file = new File(WWW_ROOT . 'files/' . $image['Image']['filename']);
return $file->delete();
}
}
Obviously this isn’t a perfect implementation, so feel free to take from it, learn from it, adapt it.
This was written off-the-cuff for a project recently where there’s only one model that has images attached, but on a larger project I’d more than likely wrap it up into a nice model behavior.
In your model you can use the afterSave callback method to handle your file upload:-
public function afterSave($created) {
if (isset($_FILES['data']['name'][$this->alias]['filename'])) {
$filename = $_FILES['data']['name'][$this->alias]['filename'];
$fileInfo = pathinfo($filename);
$fileExt = isset($fileInfo['extension']) ? $fileInfo['extension'] : '';
$filename = $fileInfo['filename'];
$newFilename = "$filename.$fileExt";
$dir = WWW_ROOT . 'files' . DS . 'uploads';
$target = $dir . DS . $newFilename;
move_uploaded_file($source, $target);
}
}
You can use $newFilename to change the filename to something appropriate if need be (I tend to check if a file with the same name already exists and rename the new one to avoid overwriting it.

cakephp - Save file syncronized and send as attachment

Hi have a strange problem with File::write() in cakephp.
I write this code to generate a pdf view and save it on a file.
private function generate( $id ){
$order = $this->Order->read( null, $id );
$this->set('order', $order);
$view = new View($this);
$viewdata = $view->render('pdf');
//set the file name to save the View's output
$path = WWW_ROOT . 'ordini/' . $id . '.pdf';
$file = new File($path, true);
//write the content to the file
$file->write( $viewdata );
//return the path
return $path;
}
I need generate this pdf and send it as attacchment so, this is my code
public function publish ($id) {
$pdf_file = $this->generate( (int)$id );
$Email = new CakeEmail();
$Email->from(array('info#.....com' => '......'));
$Email->to('....#gmail.com');
$Email->subject('Nuovo ordine');
$filepath = WWW_ROOT . 'ordini/' . $id . '.pdf';
$Email->attachments(array('Ordine.pdf' => $filepath));
$Email->send('......');
}
The first time i run this code the email doesn't works, the email works nice only if the pdf is alredy in the folder.
I think that File::write perform some kind of async writing and when system execute Email::attachments the file ins't ready.
How can i insert a while() in this code to check file avability?
thanks a lot.
if($file->write( $viewdata ))
{
return $path;
}
using the return value wasn't enough.
I changed this line
$file = new File($path, false);
Opening file without forcing worked for me.

How do I use fixed fields in CakeDC's csvUpload behavior in Util plugin

I am using the csvUpload behavior of the Utils plugin by CakeDC, on a CakePHP 2.2.1 install.
I have it working great it's processing a rather large csv successfully. However there are two fields in my table / Model that would be considered fixed, as they are based on ID's from from associated models that are not consistent. So I need to get these fixed values via variables which is easy enough.
So my question is, how do I use the fixed fields aspect of csvUpload? I have tried that following and many little variation, which obviously didn't work.
public function upload_csv($Id = null) {
$unique_add = 69;
if ( $this->request->is('POST') ) {
$records_count = $this->Model->find( 'count' );
try {
$fixed = array('Model' => array('random_id' => $Id, 'unique_add' => $unique_add));
$this->Model->importCSV($this->request->data['Model']['CsvFile']['tmp_name'], $fixed);
} catch (Exception $e) {
$import_errors = $this->Model->getImportErrors();
$this->set( 'import_errors', $import_errors );
$this->Session->setFlash( __('Error Importing') . ' ' . $this->request->data['Model']['CsvFile']['name'] . ', ' . __('column name mismatch.') );
$this->redirect( array('action'=>'import') );
}
$new_records_count = $this->Model->find( 'count' ) - $records_count;
$this->Session->setFlash(__('Successfully imported') . ' ' . $new_records_count . ' records from ' . $this->request->data['Model']['CsvFile']['name'] );
$this->redirect(array('plugin'=>'usermgmt', 'controller'=>'users', 'action'=>'dashboard'));
}
}
Any help would be greatly appreciated as I have only found 1 post concerning this behavior when I searching...
I made my custom method to achieve the same task. Define the following method in app\Plugin\Utils\Model\Behavior
public function getCSVData(Model &$Model, $file, $fixed = array())
{
$settings = array(
'delimiter' => ',',
'enclosure' => '"',
'hasHeader' => true
);
$this->setup($Model, $settings);
$handle = new SplFileObject($file, 'rb');
$header = $this->_getHeader($Model, $handle);
$db = $Model->getDataSource();
$db->begin($Model);
$saved = array();
$data = array();
$i = 0;
while (($row = $this->_getCSVLine($Model, $handle)) !== false)
{
foreach ($header as $k => $col)
{
// get the data field from Model.field
$col = str_replace('.', '-', trim($col));
if (strpos($col, '.') !== false)
{
list($model,$field) = explode('.', $col);
$data[$i][$model][$field] = (isset($row[$k])) ? $row[$k] : '';
}
else
{
$col = str_replace(' ','_', $col);
$data[$i][$Model->alias][$col] = (isset($row[$k])) ? $row[$k] : '';
}
}
$is_valid_row = false;
foreach($data[$i][$Model->alias] as $col => $value )
{
if(!empty($data[$i][$Model->alias][$col]))
{
$is_valid_row = true;
}
}
if($is_valid_row == true)
{
$i++;
$data = Set::merge($data, $fixed);
}
else
{
unset($data[$i]);
}
}
return $data;
}
And you can use it using:
$csv_data = $this->Model->getCSVData($this->request->data['Model']['CsvFile']['tmp_name'], $fixed);
Here $csv_data will contain an array of all of those records from the csv file which are not empty and with the fixed field in each record index.
So as I was telling Arun, I answered my own question and figured it out. I was looking to broad instead of really examining what was in front of me. I started running some debugging and figured it out.
First of all, $unique_add = 69 is seen as an int, duh. In order for it to be added to the csv it need to viewed as a string. So it simply becomes, $unique_add = '69'.
I couldn't enter the value of $Id directly into the fixed array. So I just had to perform a simple find to get the value I needed.
$needed_id = $this->Model->find('first', array(
'condition'=>array('Model.id'=>$Id)
)
);
$random_id = $needed_id['Model']['id'];
Hopefully this won't be needed to help anyone because hopefully no one else will make this silly mistake. But one plus... Now there's actually more than one post on the internet documenting the use of fixed fields in the CakeDC Utils plugin.

Resources