CakePHP: Updating multiple records with hasMany - cakephp

I'm working on a website that has a file management portion where users can create folders and upload files. The folders CAN have subfolders. The folders are not actually created on the file system; they are just in the database. The files are created on the file system and information about the files are in the database.
I'm trying to make it so that if a user deletes a folder, it marks that folder as well as its subfolders and files as deleted. So let's say a user deleted a folder called "Main" that had this structure:
Main
Main\Subfolder\file.txt
Main\Subfolder 2 <-- empty folder
Main\Subfolder 3\image.jpg
I am able to mark all of the folders' deleted field with a "Y" like this:
foreach ($folders_to_delete as $folder_to_delete) {
$updateAll_conditions['OR'][] = array('id' => $folder_to_delete);
}
$this->UserFolder->updateAll(array('UserFolder.deleted' => "'Y'"), $updateAll_conditions)
But I want to mark all of the folders' deleted field with a "Y" AND all of the files that belong to those folders... with one query. Is that possible?

This problem becomes even more complicated when you consider that a folder might contain another folder!
You ultimately will want a script that can handle file structures of arbitrary complexity.
I suggest running a recursive function over the file structure in question and add the records that need updating to a massive $this->data array.
Then, do a saveAll (or updateAll) on that array.
Here's what a $this-data array might look like for a saveAll with multiple models affected:
$this->data = array(
'Folder' => array(
0 => array('deleted' => 'Y'),
1 => array('deleted' => 'Y'),
),
'File' => array(
0 => array('deleted' => 'Y'),
1 => array('deleted' => 'Y'),
),
)
I'll keep this answer to the model related stuff, if you want help w/ the recursive function, let's do that that in a separate question.

Related

TYPO3 TCA Overriding exists file

I have an issue like this with TYPO3.
I have an object, this object has file attribute, this field named "pdf"
In the TCA this field I defined like this:
'pdf' => array(
'exclude' => 1,
'label' => 'LLL:EXT:locations/Resources/Private/Language/locallang_db.xlf:tx_locations_domain_model_location.pdf',
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
'files', array(
'appearance' => array(
'createNewRelationLinkTitle' => 'LLL:EXT:cms/locallang_ttc.xlf:images.addFileReference',
),
'size' => 1,
'minitems' => 0,
'maxitems' => 1,
), $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
)
),
And now I can upload a file or image for this field, but there are some things does not good:
I want to allow PDF files only
After upload one file, if I upload another file, is said that "The existing files be overwritten" but old file never be overwritten.
The new one is also not uploaded.
What I need for this case: If I upload a new file, the old file will be overwritten by the new one.
Thanks for your help.
Since you pass in $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] you are currently allowing all kind of image-like file extensions. This is the point where you might want to use 'pdf' instead.
If you want to overwrite files, you'll need to to this in the File module of TYPO3. If you want to put a different file in your relation, you can remove the current one and add another one. The first file won't be deleted automatically but your record will have a relation to the new file.

Uploading multiple pictures from the same form in CakePHP 1.2

I have a one-to-many relationship where one House can have many Pictures. The models that I have are "House" and "Picture". I am using CakePHP 1.2 and I can upload one picture successfully by using this:
echo $form->input('Picture.filename', array('type' => 'file', 'label' => __l('Image')));
In my houses_controller.php file, I have this code to save the picture and corresponding association with its house:
$this->House->Picture->save($this->data['Picture']);
But I need to save several pictures now. I read the documentation at https://book.cakephp.org/1.2/en/The-Manual/Core-Helpers/Form.html and based on that information, I am trying to use this:
echo $form->input('Picture.0.filename', array('type' => 'file', 'label' => __l('Image 1'));
echo $form->input('Picture.1.filename', array('type' => 'file', 'label' => __l('Image 2'));
Then my houses_controller.php file has this:
$this->House->Picture->saveAll($this->data['Picture']);
I see that one new record is saved in my pictures table, but I was expecting to see two new entries in my table because I should have added two pictures, not only one. Any ideas about why this may not be saving two pictures for me? Thank you.
Solution:
$this->Deal->Picture->save($this->data['Picture'][0]);
$this->Deal->Picture->save($this->data['Picture'][1]);
Using $this->data['Picture'] was not sufficient because the array was returning multiple elements, so I had to reference each with the corresponding index such as $this->data['Picture'][0] for the first element, $this->data['Picture'][1] for the second one, etc.
NOTE: It was not saving two records for me, even thought I was using save() twice. But that was another issue. You can read the answers at Save multiple times in Cakephp for the corresponding solution. Basically you need to use create() before you use save().

Multiple image upload - Meio Upload

I am using the 'MeioUpload' plugin found here 'https://github.com/jrbasso/MeioUpload' and Cakephp 2.x.
Currently using this for single image uploads, please can anyone give advice on how to handle multiple image uploads using this plugin. Currently the db table storing the images holds filename, dir, mimetype and filesize fields for each image. I want to store more than one image for each of my posts when adding a new post. Any help would be much appreciated, thanks in advance :).
As I mentioned in my comment, you might want to try https://github.com/josegonzalez/upload as MeioUpload is now deprecated, and it's developer is working on that new upload plugin I linked to.
Either way, the following info for MeioUpload holds true for the new plugin, too.
MeioUpload is built to handle one uploaded file per corresponding set of fields. I don't think the example in MeioUpload's ReadMe is ideal, as it seems to imply that you have to have a table of 'images', where as in reality, you can have a table of just about anything, where each record holds one or more uploaded files (be it images, PDF's, MP3's... anything).
So, with that in mind, you have two solutions:
1) If your posts will have a potentially infinite number of images (ie, not a fixed, small number) then you can have Posts and Images in separate tables, and set up a hasMany relationship between them. See http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
2) If you know that each post will only have a max of say 3 or 4 (or some other relatively small number) of images, then you can implement 3 (or 4, or X) sets of image fields in your Posts table / model, each to handle a separate upload. They'd be named, eg. featured_image_filename, feautred_image_dir, etc; image2_filename, image2_dir, image2_mimetype, etc; image3_filename, image3_dir, etc.
Your acts as would look something like:
var $actsAs = array(
'MeioUpload.MeioUpload' => array(
'featured_image_filename' => array(
'fields' => array(
'dir' => 'featured_image_dir',
'filesize' => 'featured_image_filesize',
'mimetype' => 'featured_image_mimetype'
),
),
'image2_filename' => array(
'fields' => array(
'dir' => 'image2_dir',
'filesize' => 'image2_filesize',
'mimetype' => 'image2_mimetype'
),
),
'image3_filename' => array(
'fields' => array(
'dir' => 'image3_dir',
'filesize' => 'image3_filesize',
'mimetype' => 'image3_mimetype'
),
),
)
);
This second solution is hardly ideal database design, but sometimes when you know there'll never be more than a few images, it's just the easiest way to do it - both in terms of developing, and in terms of an easy to use UI.
Make sense?

Multiple models and uploading files

At first - I'm new to the Yii Framework. I did some research on my own but I couldn't find a precise solution to my issue.
Assume there are two related models - Product and Image. A single Product may have multiple Images assigned. What is the best approach at creating the create / update forms that would be able to manage this kind of scheme?
The Image model consists of various fields, along with a path to the image file, so it's not just a "container" for the path itself. What's more - I need to have a thumbnail generated for every uploaded image and its path stored within the same model.
What I need to achieve is pretty much similar to the admin inline functionality known from Django - there should be a section in the Product create / update form which would allow users to add / modify / delete Images.
I tried the multimodelform extension but I couldn't get file uploading to work. What's the best way of getting it done and not having to build the whole file-upload-enabled-multiple-model-form structure manually?
The detailed solution depends on if you are using CActiveForm or CHtml form. Since you have 2 related models I assume you are using CActiveForm and will point out some thing you need to keep in mind.
For this example i am gonna assume some definitions
Product with fields id, name
Product with ONE to MANY relation to 'images' on ProductImage
ProductImage with fields id, productId, path
I also assume there gonna be 1 upload / edit, but multi delete
Here's the view:
$form = $this->beginWidget(
'CActiveForm',
array(
'id' => 'upload-form',
'enableAjaxValidation' => false,
'htmlOptions' => array('enctype' => 'multipart/form-data'),
)
);
echo $form->labelEx($product, 'name');
echo $form->fileField($product, 'name');
echo $form->error($product, 'name');
echo $form->checkBoxList($product, 'path', $product->images);
echo $form->labelEx($productImage, 'path');
echo $form->fileField($productImage, 'path');
echo $form->error($productImage, 'path');
$this->endWidget();
And your action
public function actionUpdate($productId) {
$product = Product::model()->findByPk($productId)->with('images');
$productImage = new ProductImage();
if(isset($_POST['Item']))
{
$product->attributes=$_POST['Product'];
foreach($product->images as $im) {
if(in_array($im->path, $_POST['Item']['Image']))
$im->delete();
}
$productImage->image=CUploadedFile::getInstance($productImage,'path');
if($productImage->save())
{
$productImage->image->saveAs('some/new/path');
// redirect to success page
}
}
$this->render('update', array(
'product'=>$product,
'productImage'=>$productImage,
));
}
Now note that this solution is not tested so there will be bugs, but it should give you an idea on how to write your own form.
Resources:
http://www.yiiframework.com/wiki/2/how-to-upload-a-file-using-a-model/
http://www.yiiframework.com/wiki/384/creating-and-updating-model-and-its-related-models-in-one-form-inc-image

How do I get zend to recognize the paths to Propel ORM

I am trying to get Propel to work in my Zend app, it seems that I can get the Propel library to load (from library/propel) but when called I get the exception: 'No connection information in your runtime configuration file for datasource [default]' (when I try to make a connection: 'Propel::getConnection'). My Db is not even named 'default'. I have this in my bootstrap.php from another SO question/answer:
require_once 'propel/Propel.php';
Propel::setConfiguration($this->getOptions('propel/MyPropel-conf.php'));
Propel::initialize();
// so we can get the connection from the registry easily
return Propel::getConnection();
I want the Propel configs (classmap conf as well) to be in the '/application/configs' (copies are there too right now), but I thought If I can get Propel to load from library/propel, then maybe moving my 'conf' files there, I may get them to load too. It seems that if I 'force' the config, by manually loading the params, or if I seem to get it in a temporary 'right' location (or use an absolute path), the exception I then get is this:
'Unable to open PDO connection [wrapped: SQLSTATE[28000] [1045] Access denied for user 'www-data'#'localhost'
As if Propel is not paying any attention to my configs.
My config looks like this; converted from the xml:
$conf = array (
'datasources' =>
array (
'unmActTestDB' =>
array (
'adapter' => 'mysql',
'connection' =>
array (
'dsn' => 'mysql://root:PASSWORD#localhost/unmActTestDB',
),
),
'default' => 'unmActTestDB',
),
'log' =>
array (
'ident' => 'propel-act',
'level' => '7',
),
'generator_version' => '1.5.6',
);
$conf['classmap'] = include(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'classmap-unmActTestDB-conf.php');
return $conf;
If it helps, I still have the Zend PDO DB adapter loading in the application.ini file too, would that cause a clash?. Is there a standard way to get Propel to work with Zend? Or can anyone see what I'm doing wrong?
I have been to several posts here on SO, and a couple popular posts like this one The Adventures Of Merging Propel With Zend Framework and this one at Zend's dev zone Integrating Propel with the Zend Framework, among others including the Propel Docs. They have been helpful, but I am really struggling with this. Thanks in advance! My current Zend Directory structure looks like this (w/ the two propel confs also in the library/propel folder:
What I ended up doing is this:
I got a good grip on how to generate my models from a 'reverse' using Propel. It created (as before) a 'schema.xml' file for me to build my models with.
Also my 'runtime.xml' file was incorrect for my needs, after the build I was not connecting to the right database because of my omission of a few tags, overall it is quite simple though:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<log>
<ident>unmActTestDB</ident>
<level>7</level>
</log>
<propel>
<datasources default="unmActTestDB">
<datasource id="unmActTestDB">
<adapter>mysql</adapter>
<connection>
<dsn>mysql:host=localhost;dbname=unmActTestDB</dsn>
<user>zend</user>
<password>PASSWORD</password>
<database>unmActTestDB</database>
</connection>
</datasource>
</datasources>
</propel>
</config>
The 'dsn' tag has to be in the format above with the addition of the 'user', 'password', and the 'database' tags. This fixed my issue with the database connection errors. As quoted above (and here) the exception thrown was: 'No connection information in your runtime configuration file for datasource [default]'
As far as the loading of my models goes, I ended up putting them in my 'library' folder, I already have that folder autoloading in my app, plus it seemed like a good place form them.
here is an image of my 'updated' directory structure:
Note the addition of the 'unmActTestDB' folder in my directory, this is my ORM models.
Another thing to note is that I put my generated 'conf' files into my 'application/configs' folder. These are correct now, after the correction of the runtime.xml file and a 'rebuild'.
As a side note, I had to edit my 'schema.xml' file by hand (several times :) )...The original database used plural names for the tables, so I edited all the 'phpname' declarations (attributes actually, on the declaration tag) to be singular so I wouldn't access an object called 'Users'...instead I can now access a 'User' object. I kept the table names the same (tables are plural, and I won't have any issues importing the existing data, etc.) This was suggested by an answer to another one of my questions, see here How to get related object Propel ORM.
The other big edit I made was to add primary key declarations (again, attributes) for the many SQL views in the DB, also I added a 'readonly' and a 'noSQL' attributes to the declarations, this way I will have access (through the Propel models) to my views.
And, just to be thorough, and for those who are interested here is the addition to my 'bootstrap.php' file that does my 'Propel Init' for me...
protected function _initPropel()
{
$this->_logger->info('Bootstrap ' . __METHOD__);
require '../library/propel/Propel.php';
Propel::init(APPLICATION_PATH . '/configs/unmActTestDB-conf.php');
Propel::initialize();
return Propel::getConnection();
}
Another NOTE: the '$this->_logger->info('Bootstrap ' . METHOD);' calls my 'logging' method that just tells 'firePHP' that this method has loaded. The /configs/unmActTestDB-conf.php' calls the second 'conf' file that Propel generates... and here is the 'corrected' version of that file (the unmActTestDB.conf file that is), Note the changes in the 'connection' array.
<?php
// This file generated by Propel 1.5.6 convert-conf target
// from XML runtime conf file runtime-conf.xml
$conf = array (
'datasources' =>
array (
'unmActTestDB' =>
array (
'adapter' => 'mysql',
'connection' =>
array (
'dsn' => 'mysql:host=localhost;dbname=unmActTestDB',
'user' => 'zend',
'password' => 'PASSWORD',
'database' => 'unmActTestDB',
),
),
'default' => 'unmActTestDB',
),
'log' =>
array (
'ident' => 'unmActTestDB',
'level' => '7',
),
'generator_version' => '1.5.6',
);
$conf['classmap'] = include(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'classmap-unmActTestDB-conf.php');
return $conf;
I am off and running now! This was a great way to go, otherwise I was looking and writing sooo many mapping classes for my app. Propel (currently) generates over 300 models for this application! Plus the base classes, it would take me forever...

Resources