Cakephp 3.0 multiple files uploaded url - cakephp

I am using 3.4 cakephp version but file uploaded successfully but i want the url to be updated in request entity. I can have one entity updated but not all like:
$post->url = WWW_ROOT.'uploads/filename';
Html form :
<?= $this->Form->create(null, ['class' => 'form-horizontal', 'id' => 'postSubmit', 'type' => 'file', 'autocomplete' => 'off'])?>
<div class="form-group">
<label for="inputEmail3" class="col-sm-4 control-label">Ad Category:</label>
<div class="col-sm-8">
<?= $this->Form->select('category_id', $categories, ['class' => 'form-control required categorySelected', 'empty' => 'Select Category'])?>
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-4 control-label">Photos(5 max):</label>
<div class="col-sm-8">
<?= $this->Form->input('images.', [ 'type' => 'file', 'id' => 'image', 'multiple' => 'multiple', 'accept' => 'image/jpg, image/jpeg', 'label' => false])?>
<p class="help-block">Good photos quick response</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-8 col-sm-offset-4">
<?= $this->Form->button('Post Now', ['class' => 'btn custom_btn btn-sm', 'type' => 'submit', 'label' => false])?>
</div>
</div>
</form>
Controller Action :
$post = $this->Posts->newEntity();
$post = $this->Posts->patchEntity($post, $this->request->getData());
if($this->request->getData()['images']) {
$dir = new Folder(WWW_ROOT.'uploads', true, 0755);
$files = $this->request->getData()['images'];
foreach ($files as $key => $file) {
if($file['error'] == 0) {
$info = pathinfo($file["name"]);
$newfilename = $info["filename"].'_'.time() . '.'. $info["extension"];
if(move_uploaded_file($file["tmp_name"], WWW_ROOT.'uploads/' . $newfilename)) {
$post->url = WWW_ROOT.'uploads/' . $newfilename;
}
}
}
}
if ($this->Posts->save($post)) {
$this->Flash->success('Post has been submitted successfully. Please wait
for approval');
}

I recommend you to implement the CakePHP3-Proffer described bellow:
Uploading multiple related images
This example will show you how to upload many images which are related to your current table class. An example setup might be that you have a Users table class and a UserImages table class. The example below is just baked code.
Tables
The relationships are setup as follows. Be sure to attach the behavior to the table class which is receiving the uploads.
// src/Model/Table/UsersTable.php
$this->hasMany('UserImages', ['foreignKey' => 'user_id'])
// src/Model/Table/UserImagesTable.php
$this->addBehavior('Proffer.Proffer', [
'image' => [
'dir' => 'image_dir',
'thumbnailSizes' => [
'square' => ['w' => 100, 'h' => 100],
'large' => ['w' => 250, 'h' => 250]
]
]
]);
$this->belongsTo('Users', ['foreignKey' => 'user_id', 'joinType' => 'INNER']);
Entities
Your entity must allow the associated field in it's $_accessible array. So in our example we need to check that the 'user_images' => true is included in our User entity.
Controller
No changes need to be made to standard controller code as Cake will automatically save any first level associated data by default. As our Users table is directly associated with our UserImages table, we don't need to change anything.
If you were working with a related models data, you would need to specify the associations to populate when merging the entity data using the 'associated' key.
Templates
You will need to include the related fields in your templates using the correct field names, so that your request data is formatted correctly.
// Don't forget that you need to include ['type' => 'file'] in your ->create() call
<fieldset>
<legend>User images</legend>
<?php
echo $this->Form->input('user_images.0.image', ['type' => 'file']);
echo $this->Form->input('user_images.1.image', ['type' => 'file']);
echo $this->Form->input('user_images.2.image', ['type' => 'file']);
?>
</fieldset>
How you deal with the display of existing images, deletion of existing images, and adding of new upload fields is up to you, and outside the scope of this example.

Related

CakePHP 4 Saving Data Twice

CakePHP is somehow saving the same data twice. For some reason I want to implement this add method such that $dummy is saved right away as someone goes directly to domain.com/recordings/add
It looks pretty straight forward and I've been scratching my head. I've checked for validation errors; I've tried disabling validation; I've tried using patchEntity() instead.
Though, one strange thing is that, if you go to domain.com/recordings/add by hitting the add recording button in domain.com/recordings/index (instead of typing the url out on the browser), the data is saved just once.
Controller:
public function add()
{
$dummy = [
"user_id" => 1,
"title" => "tgfbthgdthb",
"body" => "rgcvfghfhdxcgb",
"published" => 0,
];
$recording = $this->Recordings->newEntity($dummy);
$this->Recordings->save($recording);
}
Model/table:
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('recordings');
$this->setDisplayField('title');
$this->setPrimaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER',
]);
$this->hasMany('Words', [
'foreignKey' => 'recording_id',
]);
}
Model/entity:
protected $_accessible = [
'user_id' => true,
'title' => true,
// 'slug' => true,
'body' => true,
'published' => true,
'created' => true,
'modified' => true,
'user' => true,
'words' => true,
];
The view:
<?php
/**
* #var \App\View\AppView $this
* #var \App\Model\Entity\Recording $recording
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Html->link(__('List Recordings'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="recordings form content">
<?= $this->Form->create($recording) ?>
<fieldset>
<legend><?= __('Add Recording') ?></legend>
<?php
echo $this->Form->control('user_id', ['options' => $users]);
echo $this->Form->control('title');
echo $this->Form->control('body');
echo $this->Form->control('published');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</div>
Do not try to accommodate to people's lazyness, do not allow them to save data by simply visiting a URL, ie via a GET request, that's just going to cause trouble, and on top of that it's bad application design.
Implement at least a proper safeguard that only saves data for POST requests.
Browsers may issue multiple requests in various scenarios, from preflight OPTIONS requests to weird quirks, like for example Firefox aborting the request if doesn't find any encoding information in the first x bytes of response data, and then issuing a new request that assumes a specific encoding for the response.
public function add()
{
if ($this->request->is('post')) {
// save data here
}
}

How to add associated data while adding User simultaneously CakePHP 3.x?

I would like to be able to add a membership record at the same time I add a user. I can't seem to get it to work. I want to be able to select the membership level and have that data get sent to the controller where I can add the data, unless there is a way to just select the membership level and it will automatically create a membership record for the user that gets added.
The membership level contains the information for that level of membership and the membership record contains some information from the membership level and is associated to a user.
The add form I believe is where the problem lies, but just in case here are the important snippets of code. I tried to keep it as simple as possible.
Users Table Initialize Function:
public function initialize(array $config)
{
parent::initialize($config);
$this->table('users');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->hasOne('Memberships', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
}
Memberships Table Initialize Function:
public function initialize(array $config)
{
$this->table('memberships');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
$this->hasOne('MembershipLevels', [
'foreignKey' => 'membership_level_id',
'joinType' => 'INNER'
]);
}
Users Controller Add Method:
public function add()
{
$user = $this->Users->newEntity($this->request->data(), [
'associated' => [
'Memberships' => ['associated' => ['MembershipLevels']]
]
]);
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data(), [
'associated' => [
'Memberships' => ['associated' => ['MembershipLevels']]
]
]);
if ($$this->Users->save($user)) {
$this->Flash->success(__('You have been added.'));
} else {
$this->Flash->error(__('You could not be added. Please, try again.'));
}
}
$membershipLevels = $this->Users->Memberships->MembershipLevels->find('list', ['limit' => 200]);
$this->set(compact('user', 'membershipLevels'));
$this->set('_serialize', ['user', 'membershipLevels']);
}
Add Users Form:
<div class="form">
<h1>Join Now</h1>
<?= $this->Form->create($user); ?>
<fieldset>
<legend><?= __('User Info') ?></legend>
<?= $this->Form->input('full_name', ['required' => false]); ?>
<?= $this->Form->input('username', ['required' => false]); ?>
<?= $this->Form->input('email', ['required' => false]); ?>
<?= $this->Form->input('password', ['required' => false]); ?>
<?= $this->Form->input('password_confirmation', ['type' => 'password', 'required' => false]); ?>
<?php
if ($current_user['role'] === 1 && isset($logged_in)) {
echo $this->Form->input('role', ['type' => 'select', 'options' => ['1' => 'Admin', '2' => 'Editor', '3' => 'Author', '4' => 'Reader'], 'default' => '4']);
}
?>
</fieldset>
<fieldset>
<legend><?= __('Membership Info') ?></legend>
<?= $this->Form->label('Membership Level'); ?>
<?= $this->Form->input('memberships.membership_levels._id', ['options' => $membershipLevels, 'required' => false]); ?>
</fieldset>
<?= $this->Form->button(__('Sign Up'));?>
<?= $this->Form->end();?>
</div>
I changed the code a bit thanks to the comments and answers. The membership levels now show up in the form and I pick a membership level when saving the user, but the membership doesn't get saved as well, just the user. Any further suggestions?
How about start reading the migration guide, or doing the blog tutorial for CakePHP3? Your trial and error approach is time consuming and frustrating. I can't recommend this enough: Always read documentation before shooting in the darkness. There has a lot changed in CakePHP3. This includes the way the dot notation is used. Reading saving associations might help as well.
You don't use any longer the model prefix on the first level.
Model.field
is now just
field
For nested associations it's now
associated_data.field
associated_data.second_level_data.field
Notice that this is inflected and singular or plural depending on the kind of association.
I have updated my code to get the closest I can to getting all the automagic to work.
Key points:
1.) In controller I got the membership levels variable to pass to the view by adding this line. (notice the calling of the users table then memberships and then membershiplevels).
$membershipLevels = $this->Users->Memberships->MembershipLevels->find('list', ['limit' => 200]);
2.) In the view I added:
<?= $this->Form->input('memberships.membership_levels._id', ['options' => $membershipLevels, 'required' => false]); ?>
This is not a complete answer however because I still have to grab the membership level in the controller with a query and then manually save the data from that level to the memberships table.
I can at least populate the membership level input in the form dynamically and submit it.
I had looked over the bookmarker tutorial that I had previously done since it uses associations, but with that tutorial they are able to add the bookmarks_tags record automatically when you add a bookmark and I am not sure if it is due to the functions in the table files or not.

How to debug "Call to a member function dirty() on a non-object" in Cakephp 3.x?

I want to do save the data, I got from form.
My error message:
Error: Call to a member function dirty() on a non-object.
My view code:
<?= $this->Form->create($album,['role' => 'form','type' => 'file'])?>
<div class="form-group">
<?= $this->Form->input( 'albumName',
[ 'class' => 'form-control',
'label' => __('Albüm Adı:'),
'placeholder' => __('Albüm Adı'),
'onkeyup' => 'javascript:setVirtualSanalUrl(this,"virtualUrl")']);?>
</div>
<div class="form-group">
<?= $this->Form->input( 'albumVirtualUrl',
[ 'class' => 'form-control',
'label' => __('Albüm Sanal Adres(Site Adres Bilgisi İçin):'),
'placeholder' => __('Albüm Sanal Adres'),
'id' => 'virtualUrl',
'onChange' => 'javascript:setVirtualSanalUrl(this,"virtualUrl")']);?>
</div>
<?php
foreach ($Languages as $lang):?>
<?php echo $lang;?>
<div class="form-group">
<?= $this->Form->input( '_translations.'.$lang.'.albumName',
[ 'class' => 'form-control',
'label' => __('Albüm Adı({0}):', [$lang]),
'placeholder' => __('Albüm Adı({0})', [$lang]),
'onkeyup' => 'javascript:setVirtualSanalUrl(this,"virtualUrl'.$lang.'")']);?>
</div>
<div class="form-group">
<?= $this->Form->input( '_translations.'.$lang.'.albumVirtualUrl',
[ 'class' => 'form-control',
'label' => __('Albüm Sanal Adres(Site Adres Bilgisi İçin)({0}):', [$lang]),
'placeholder' => __('Albüm Sanal Adres({0})', [$lang]),
'id' => 'virtualUrl'.$lang,
'onChange' => 'javascript:setVirtualSanalUrl(this,"virtualUrl'.$lang.'")']);?>
</div>
<?php
endforeach;
?>
<div class="box-footer">
<?= $this->Form->button(isset($album->id) == false ? __('Kaydet'):__('Düzenle'),
['class' => 'btn btn-primary']);?>
<?= $this->Form->end();?>
Data is records that I place:
$album = $this->ImageAlbums->newEntity();
if ($this->request->is('post')) {
$album = $this->ImageAlbums->patchEntity($album, $this->request->data);
$album->albumVirtualUrl = $this->createVirtaulUrl($album->albumVirtualUrl);
if (empty($album->errors())) {
$resultAlbum = $this->ImageAlbums->save($album);}}
This code is not working, but the one below work fine.
$example = $this->ImageAlbums->newEntity();
$example->albumName = "deneme";
$example->albumVirtualUrl = "sdsd";
$example->translation('en-US')->albumName = 'albumName';
$example->translation('en-US')->albumVirtualUrl = "albumVirtualUrl";
$example->translation('fr_FR')->albumName = "fr albumName";
$example->translation('fr_FR')->albumVirtualUrl = "fr albumVirtualUrl";
$this->ImageAlbums->save($example);
I looked at those pages: http://book.cakephp.org/3.0/en/orm/behaviors/translate.html
I did the steps. You can record data straight but I can not register out of the forum.
I know i'm a year too late but I was facing the same issue. The dirty() method expect an object, not an array.
So you've to assign the translation manually like you did.
This script should fix the problem :
foreach ($this->request->data['_translations'] as $k => $data) {
foreach ($data as $dataKey => $datum) {
$yourEntity->translation($k)->{$dataKey} = $datum;
}
}
$this->Model->save($yourEntity);

Cakephp 1.3 select field options from list or enter new

I have a cakephp 1.3 app which get's a person's consent to participate in a study. I'm trying to link inclusion criteria and exclusion criteria for the study on the Add study page. The HABTM relationship is setup and working(If there's only one way to select them) What I'm trying to do now is display the current Inclusion Criteria as a list of checkboxes the person adding the study can select, but I also want a field where new criteria can be entered as a comma seperated list. Right now it will save the checkboxes, but I want it to also add the text entered in the field as well. Screen showing the Add study as it sits now is below. I don't really know how I would go about doing that. Is there a way to do this or will I have to take the data entered, sort through it and then add it to $this->data[inclusion field data]? An example would be nice. :) I've added the code I have so far below the image.
Controller:
/**
* Add a study.
*/
function add() {
if (!empty($this->data)) {
//get the inclusions from the text data
//Sort through the comma seperated list and add it to $this->data['Study']['Inclusions']???
$this->Study->create();
if ($this->Study->save($this->data)) {
$this->Session->setFlash(__('The study has been saved', true));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The study could not be saved. Please, try again.', true));
}
}
$this->set('inclusions', $this->Study->Inclusion->find('list', array(
'fields' => array('id', 'name'),
'order' => 'Inclusion.name',
'recursive' => 0,
)));
$this->set('exclusions', $this->Study->Exclusion->find('list', array(
'fields' => array('id', 'name'),
'order' => 'Exclusion.name',
'recursive' => 0,
)));
$this->set('forms', $this->Study->Form->find('list', array('order' => 'Form.name','recursive' => 0,)));
}
View:
<div class="studies form">
<?php echo $this->Form->create('Study', array('type' => 'file'));?>
<fieldset>
<legend><?php __('Add Study'); ?></legend>
<?php
echo $this->Form->input('studyname', array('label'=>'Study Name','required' => true));
echo $this->Form->input('studynumber', array('label'=>'Study Number','required' => true));
echo $this->Form->input('file', array('label'=>'Select Electronic Consent','type' => 'file'));
echo $this->Form->input('consent_form_date');
?>
<fieldset class="inclusion">
<legend><?php __('Inclusions'); ?></legend>
<div class="IncExc">
<?php
echo $this->Form->input('Inclusion',array(
'label' => false,
'type' => 'select',
'multiple' => 'checkbox',
'options' => $inclusions,
'selected' => $html->value('Inclusion.Inclusion'),
));
?>
</div>
<?php
echo $form->input('Inclusion.inclusions',array(
'type' => 'text',
'label' => __('Add New Inclusions',true),
'after' => __('Seperate each new Inclusion with a comma. Eg: family, sports, icecream',true)
));
?>
</fieldset>
<fieldset>
<legend><?php __('Exclusions'); ?></legend>
<div class="IncExc">
<?php
echo $this->Form->input('Exclusion',array(
'label' => false,
'type' => 'select',
'multiple' => 'checkbox',
'options' => $exclusions,
'selected' => $html->value('Exclusion.Exclusion'),
));
?>
</div>
</fieldset>
<fieldset style="width: 700px;">
<legend><?php //__('Forms'); ?></legend>
<?php /*
echo $this->Form->input('Form',array(
'label' => false,
'type' => 'select',
'multiple' => 'checkbox',
'options' => $forms,
'selected' => $html->value('Form.Form'),
));
*/ ?>
</fieldset>
</fieldset>
<?php echo $this->Form->end(__('Submit', true));?>
</div>
You are trying to do pretty much the same as he wanted to do: Saving tags into a database table in CakePHP
What you have to do is:
Check if the manual inclusion field is empty or not. If the field is empty ignore it and go to step 5
If the field is not empty, split it at comma or whatever sign you want to use as a seperator. You can now validate it if you want to.
Use all the inclusions of step 3 and save them in your custom or real inclusion table. You should keep track of those custom entries in some way, so you don't mess them up with those you provided. Save all the ids generated while saving within an array.
Merge the collected ids of step 3 with those you got from the form which were given by you.
Save all collected and given data to database
The answere in the post given above including the code there can be used to do that. You should get along with it pretty well if you read the whole post + code comments ;)
If you have any further questions, just ask ;)
Greetings
func0der

cakephp: How to set checkbox to checked?

I am using
$form->input('Model.name', array('multiple'=>'checkbox');
I am trying to base on model data to set certain checkboxes to checked.
How can i do that?
cmptrgeekken's solution works for a single checkbox. I'm assuming you're generating a multiple checkboxes, for a HABTM relation or something similar.
You need to pass a array with the values of the elements that are going to be selected to the method, like this:
$options = array(1 => 'ONE', 'TWO', 'THREE');
$selected = array(1, 3);
echo $form->input('Model.name', array('multiple' => 'checkbox', 'options' => $options, 'selected' => $selected));
is going to generate this:
<div class="input select">
<label for="ModelName">Name</label>
<input name="data[Model][name]" value="" type="hidden">
<div class="checkbox">
<input name="data[Model][name][]" checked="checked" value="1" id="ModelName1" type="checkbox">
<label for="ModelName1" class="selected">ONE</label>
</div>
<div class="checkbox">
<input name="data[Model][name][]" value="2" id="ModelName2" type="checkbox">
<label for="ModelName2">TWO</label>
</div>
<div class="checkbox">
<input name="data[Model][name][]" checked="checked" value="3" id="ModelName3" type="checkbox">
<label for="ModelName3" class="selected">THREE</label>
</div>
</div>
The first and third checkbox checked.
Just remember that you're actually working with a multiple select element that is just displayed as a bunch of checkboxes (Which is IMO better because of the usability).
I don't use CakePHP, but according to the docs, it appears as though you should be able to add the option 'checked'=>true:
$form->input('Model.name', array('type'=>'checkbox','checked'=>true));
since that's one of the options of the checkbox function.
$options = array(1 => 'ONE', 'TWO', 'THREE');
$selected = array(1, 3);
echo $form->input('Model.name',
array(
"name"=>$mnus['Aco']['id'],
"type"=>"select",
"multiple"=>"checkbox",
'options' => $options,
'selected' => $selected)
);
this is the correct way for multiple check box and checked option. I am using this in cake1.3 please recheck once on your code it must work.
echo $this->Form->input('Title', array('type'=>'checkbox', 'label'=>'Label', 'checked'=>'checked'));
The Marko solution still working in CakePHP 2.0+
-> https://stackoverflow.com/a/1962499/3197383
It just need to correct with the new syntax :
<?php
$options = array(1 => 'ONE', 'TWO', 'THREE');
$selected = array(1, 3);
echo $this->Form->input('ModelName',
array('multiple' => 'checkbox', 'options' => $options, 'selected' => $selected)
);
?>
Its Super Simple
$form->input('field_name', array('type'=>'checkbox','checked'=>true));
That's it.
Documentation: https://book.cakephp.org/3.0/en/views/helpers/form.html
Another way to have a checkbox checked with the "label" right next to it is.
$form->checkbox('Model.name', array('checked'=>'checked'))?> Label
Label can be what ever you want though. example: 21,000-3000, Tire, Human. I am sure you get the idea.
<?php
$subjects = array(1=>'Snow boarding',2=>'Surfing',3=>'Trekking',4=>'Swimming');
$selected_skills = array(0=>2,1=>4);
// For MutiSelect box with selected
$form->input('skills_list',array('label' => 'Skills','options' => $subjects,'class' =>'','multiple'=>true,'selected'=> $selected_skills));
//For Multiple checkbox with checked
$form->input('skills_list',array('label' => 'Skills','options' => $subjects,'class' =>'','multiple'=>'checkbox','selected'=> $selected_skills));
?>
Here is a small code snippet from one of my project-
$categories = $this->Site->Category->find('list', array('recursive' => -1));
$this->set(compact('categories'));
$this->Site->Category->bindModel(array('hasOne' => array('CategoriesSite')));
$selected = $this->Site->Category->find('list', array(
'fields' => array('id'),
'conditions' => array(
'CategoriesSite.site_id' => $this->data['Site']['id'],
),
'recursive' => 0,
));
$this->set(compact('selected'));
Main key is for selected is 'fields' => array('id')
$options = array("fixed","varry");
$selected = "0";
echo $form->input('Model.name', array('multiple' => 'checkbox', 'options' => $options, 'value' => $selected));
Use the value attribute to make checked default.
'likes_preferences' =>array(
'type'=>'select','label' => 'Main likes/preferences',
'options' => $this->Ethos->toOptionsArray('preferences'),
'multiple' => 'checkbox',
'div'=>array('class'=>'input select checkbox-group clearfix'),
'hiddenField' => false,
),
the above code for adding the data, you need to change the field 'likes_preferences' from array to comma separated string before saving into database.
$preferences = implode(',',$this->request->data['Member']['likes_preferences']);
$this->request->data['Member']['likes_preferences'] = $preferences;
EDIT MODE
$likes = explode(',',$this->request->data['Member']['likes_preferences']);
'likes_preferences' =>array(
'type'=>'select','label' => 'Main likes/preferences',
'options' => $this->Ethos->toOptionsArray('preferences'),
'multiple' => 'checkbox',
'div'=>array('class'=>'input select checkbox-group clearfix'),
'hiddenField' => false,
'selected' => $likes
),
you are done, again you must convert the array to string while updating the database in edit action.
You can also try this for input with single option
$this->Form->input('name', array('type' => 'checkbox', 'default' => 1, 'checked' => 'checked'));

Resources