How to store files temporarily using Laravel? - php

Using Laravel 5.2, I am working on an upload form which should allow the user to select up to 10 images, display their previews and then let him sort them arround, add more images or remove the ones he doesn't want to upload.
The sorting and removal of previews can quite easily be done using javascript but beyond that I am having a hard time figuring out how to properly do the rest. My original idea was to get the js file array from the input, move it to another array and work with that once the user interacts with the image previews, which would be displayed using FileReader.
Once he submits the form I would create a FormData object, remove the current files in input and add all the valid ones:
var formdata = new FormData(document.getElementById('upload-form'));
if (formdata.has('pic[]') && validFiles.length) {
formdata.delete("pic[]");
var len = validFiles.length;
for (i = 0; i < len; i++) {
formdata.append("pic[]", validFiles[i]);
}
}
If you know your way around js you can already see that this approach has severe problems when it comes to backwards compatibillity, as the FormData.delete(..), .append(..) and .has(..) methods are not supported by older browsers (including Chrome 50 and below or pretty much any IE.)
Ok scrap that idea. My second idea was to do the following:
User selects images to add
Upload said images using ajax and store them temporarily somewhere using Laravel
Return image previews
Let the user use the previews to reorder the files or remove them and keep track of what he does.
Once the form is submitted take all the other info, figure out which files are to be stored forever and which ones to delete and save.
Now I am not quite sure how to do this. I was thinking that I coud have a tmp directory for these images, generate some sort of a string to be the name of the directory associated with the users session, store the files there and save the directory name using Session.
The problem with this is that Session seems to have a global timeout and cannot be set to expire in say 15 minutes. Another problem I see is that once the Session expires and the user decides to not upload the images they would stay on the server forever as I couldn't find a method that would allow me to execute code upon Session expiry.
So what is the proper way to do temporary file storage in Laravel?
Or am going in the wrong direction here and can you see a better way to do what I am attempting to do?
Thanks in advance for any advice.

Your second idea seems to be the way to go, but slightly modified.
How about this:
<?php
// Collect a list of files in a $tempLocation (modify this to your usage)
collect(\File::files($tempLocation))->map(function($file) {
return [
'file' => $file,
'basename' => pathinfo($file)['filename']
];
// Filter out the non-numeric files and those that are before now
})->filter(function($a) {
return is_numeric($a['basename']) && $a['basename'] < time();
// Delete each file that is outdated
})->each(function($file) {
try { unlink($file['file']); } catch($e) { /* Fill me in */ }
});
Your usual workflow would then be to upload your users' files with the nomenclature:
{timestamp + timeout}.{ext}
You'd then put the above function in a daily or weekly cron. All the "old" files would be deleted. If your user wants to keep a file simply move the file elsewhere, in a non-temporary directory, or rename it to something non-numeric.
This also means the frontend won't need to take care of deleting the files or notifying your backend that they need to be discarded.
I hope this helps.

Related

Removing any existing file from Dropzone shows dictDefaultMessage

I've created a dropzone showing existing files on the server. I've added remove links which work. My problem is that when I remove a file with its remove link, the default "Drop files here to upload" message appears in the dropzone even though there are still thumbnails remaining.
I've followed this tutorial and updated with
myDropzone.emit("addedfile", mockFile);
// And optionally show the thumbnail of the file:
myDropzone.emit("thumbnail", mockFile, "/image/url");
// Make sure that there is no progress bar, etc...
myDropzone.emit("complete", mockFile);
from Enyo's FAQ.
Why would this be happening?
On a dropzone with no existing files, this message only appears when the last file is removed.
All help appreciated.
Cheers,
Tane
Ok I've figured it out, no need to add code to manually change class names. Just add this when you're adding your mockfiles.
mydropzone.files.push(mockFile);
This files array is what dropzone is using to determine if the dropzone is empty or not. And when I was just changing the length property, code was crashing trying to access an array that was never filled.
It would be great if you provide some working code, for example on jsfiddle.net, to have better understanding what are you trying to describe here.
If I got it right, you need to catch event that there are no files to show, which is whenever you data variable is empty in init function on javascript side when the dropzone is loaded.
Also this should be handled in events thumbnail or addedfile and with removedfile. Here you can count how many files are currently available.

How to wp_dequeue_script or wp_deregister_script jquery (not effected) file from wp theme

NOTE: I already use wp_dequeue_script or wp_deregister_script but not successfull
Here is the scenario, i make a image slider plugin that use jquery-cycle2 and it work successfully.
There is a user who used a wp theme and in theme there is a jquery-cycle1, now when he install my plugin the jquery-cycle1 and jquery-cycle2 conflicts, when user delete the jquery-cycle1 file all things work fine and perfectly but i don't want to delete file by user.
Now i am trying that when user install my plugin the jquery-cycle1 in theme close or deregister or stop its effect.
I get file from theme successfully
if((file_exists($theme_file_path."/jquery.cycle.all.js"))){
echo "yes";
}
but i have no idea to close jquery-cycle1 file or stop its effect.
Last Option: I have last solution that delete the file from theme but its my last option.
Please any suggestions, help me.
You will have to place an incompatibility notice on your theme.
It is not possible to attempt to detect the existence of script from server side. You are able to detect queued scripts via the word press methods, however, this assumes that the user has not simply linked the file with a <script></script> tag. The file_exists method assume the file is stored on the server itself - it could be linked from a CDN or another server.
Also, whatever methods you use to detect and remove jQuery-Cycle; You are going to break any feature on the site that uses the existing plugin.
Thus, any solution you able to devise would either be extremely complicated, or would not be generalised enough to account for all these possibilities.
You may be able to use the following to prevent loading your script
if (jQuery().cycle) {
// Script already loaded
console.log("Error: Another version of jQuery-Cycle is already loaded!");
} else {
// Load your script
}
but this cannot unload what is already loaded.
There is a simple hack you can do on you end. In the Cycle2 source replace all $.fn.cycle with $.fn.cycle2 and ).cycle( to ).cycle2(.
I am using the source from here http://malsup.github.io/jquery.cycle2.js
After that You can access it like
$("#id").cycle2({option})
Here is a demo http://jsfiddle.net/33g6z79h/
Here i assume that you are not using cycle events http://jquery.malsup.com/cycle2/api/#events
and cycle extra transitions.
If you are using it you can make a fiddle for your cycle2 implementation and i would be glad to help :)

Agile Toolkit: Any way to upload files and fetch directly from $_FILES['uploadedfile']['tmp_name']?

I wrote a simple script in plain PHP that uses $_FILES['uploadedfile']['tmp_name'] to fetch a freshly uploaded file and process its contents directly without permanently storing it. The idea is to allow the user to upload a file containing several rows of data that will be automatically parsed and added to a database by the PHP script. There is no need to store the file itself or a reference to it in the database as only the file contents are important. I know of Import CSV to MySQL, but I am trying to keep things clean and easy for the user (and for the time being I am developing with phpDesktop + sqlite so that my application will be portable).
I am now trying to recreate this process within Agile Toolkit but I cannot seem to figure out how. I know that the filestore model must access ['tmp_name'] before it moves/renames the file but I cannot figure out how to poach just this functionality. I tried looking in /lib/Form/Field/Upload.php to see if any of the methods there might be of use, but I am quite new to PHP so these docs are baffling to me. getFilePath() looked promising, but it seems that $_FILES remains empty when I do something like:
$form = $page->add('Form');
$upl = $form->addField('Upload', 'file');
$form->addSubmit();
if ($form->isSubmitted()){
$form->js()->univ()->alert($upl->isUploaded())->execute(); //sends js alert('false')
}
I realize that AJAX cannot be used to post files and I have a feeling this is part of the problem but I am not really sure where to go from here. Any help would sincerely be appreciated.
Thanks.
Agile Toolkit uploads file as soon as it is selected - moves it immediately into filestore and creates database record, so not really what you need.
Anything you write in a plain PHP can also work with Agile Toolkit. You can disable JavaScript in the form by doing this:
$this->add('Form',array('js_widget'=>false));
Such a form would send you a normal POST request.
Ok, I managed to achieve what I wanted by creating a custom extension of Form_Field_Upload and redefining the loadPOST() method within it.
function loadPOST(){
Form_Field::loadPOST();
if($_GET[$this->name.'_upload_action']){
// This is JavaScript upload. We do not want to trigger form submission event
$_POST=array();
}
if($_GET[$this->name.'_upload_action'] || $this->isUploaded()){
if($this->model){
try{
$model=$this->model;
/*Function is identical to parent above this line*/
//process file here and do $model->set('custom_field',$value);
//I am using $this->getFilePath() to analyze the uploaded file
/*Function is identical to parent below this line*/
$model->save();
}catch(Exception $e){
$this->api->logger->logCaughtException($e);
$this->uploadFailed($e->getMessage()); //more user friendly
}
$this->uploadComplete($model->get());
}
}
if($_POST[$this->name.'_token']){
$a=explode(',',$_POST[$this->name.'_token']);$b=array();
foreach($a as $val)if($val)$b[]=$val;
$this->set(join(',',filter_var_array($b,FILTER_VALIDATE_INT)));
}
else $this->set($this->default_value);
}
I sure this is not the most elegant solution but it worked for my purpose anyway.

Saving variables (not sessions)

This may be a silly question, but how do I save variables that are not specific to a particular session. An simple example of why you might want to do this would be a visitor counter - a number that increases by one each time someone visits a web page (note - I'm not actually doing that, my application is different, but that is the functionality I need). The only ways I can think of doing this are either writing the variables to a file, or putting the variables into a database. Both seem a bit inelegant. Is there a better way to to this kind of thing?
If you need to save global state, you need to save global state. This is typically done in either a file or a database as you already noted.
It's not "inelegant" at all. If you need to save something (semi-)permanently, you put it in a database. That's what databases are for.
Have a look at the serialize() function in PHP http://uk3.php.net/serialize where you'll be able to write an array or such to a file and re-retrieve:
<?php
// Save contents
$var = array('pageCounter' => 1);
file_put_contents('counter.txt', serialize($var));
// Retrieve it
$var = unserialize(file_get_contents('counter.txt'));
?>
Otherwise save the value to a database.
Given that PHP is stateless and that each pageload is essentially re-running your page anew, if you're going to be saving variables that will increment over multiple pageloads (e.g., number of distinct users), you'll have to use some form of server-end storage - file-based, database, whatever - to save the variable.
You could try installing APC (Alternative PHP Cache) which has cool features for sharing data between all PHP scripts, you could try using shared memory too or like you said, use a file or database
I think I've found the answer - session_name('whatever') can be used to have a fixed name for a session, I can refer to that data as well as the session specific session.
If you want it to be permanent, database and files are really your only two choices.
If you only want to temporarily store these values in memory, if APC is installed, you can do this:
// Fetch counter value back from memory
$success = false;
$counter = apc_fetch('counter', &$success);
if ($success) {
// fetch succeeded
} else {
// fetch failed
$counter = 0;
}
// Increment the counter and store again
// Note that nothing stops another request/page from changing this value
// between the fetch and store calls.
$counter++;
apc_store('counter', $counter);
That was just an example.
For a counter, you're better off using apc_inc('counter') / apc_dec('counter').
Presumably other opcode caches have similar methods. If you're not running an opcode cache... really? You want PHP to recompile a page every time its requested?
Elegant, no database and no file ?
Store it in your server memory with shmop and hope your server does not reboot !

How to setup split test?

I want to create a way to test different layouts on a page to see which get more conversions.
For example. If I have 2 versions of a page and I send 50% to page A and 50% to page B and see which one converts more sales.
So I am thinking maybe use .htaccess to rewrite half to page A and the other half to page B.
But how can I do that with .htaccess is there a way? do I need to use PHP instead to do this?
Also if there is a better way to do this, or any cautions I should be aware of, please let me know.
Lots of ways to deal with it on your own code. If however you're already using Google Analytics and don't care to use javascript for the test, spare yourself a lot of trouble and look at http://www.google.com/websiteoptimizer/index.html
Update (Reconfine): Google website optimizer no longer exists, this has been replaced with "Google Analytics content experiments" https://developers.google.com/analytics/devguides/platform/experiments-overview
I would do that using php, following way:
After the user got to the default php file, i would store his browser data in a db table, and the active layout identifier (filename, row id, etc...).
Everytime the server gets a request from this user, it shows the page, mapped to him.
But! If you have two independent pages, i would only store how many people visited site one, and site two, and redirect them to page A, and to page B by a 50% division.
If you've got a supported database and are using PHP 5.2 or later, you can use a free split testing library called phpScenario, found at www.phpscenario.org
Then you write more or less something like this:
require_once 'scenario_setup.php'; // you write this
if (Scenario::IsControl('experimentname')) {
// read and output version 1
} else {
// read and output version 2
}
Then when you get to your conversion point (say, sign up):
require_once 'scenario_setup.php'; // same
Scenario::Complete('experimentname');
And to view the stats (probably on your admin page):
require_once 'scenario_setup.php'; // yup
Scenario::RenderXml('experimentname');
I think php will be very useful. For example, you can use rand or push a variable in a file :
$int = file_get_contents('var');
if ($int) {
$int++;
} else{
$int = 1;
}
if (($int % 2) == 0) {
header('Location: url1);
} else {
header('Location: url2);
}
file_put_contents ('var', $int);
With apache, you have to setup a load balancer : http://httpd.apache.org/docs/2.1/mod/mod_proxy_balancer.html
I would use php personally. Then you can save which page layout you chose for them as a session var making it easy to load that layout on each page refresh. You would probably also want to save into the database with their username (if they login) and if they visit later show them the same layout.

Categories