So far this is what I know to be the way to pass the data from a post into the form.
$form->setData( $this->getRequest()->getPost() );
I thought that this might work
$form
->setData( $this->getRequest()->getPost() )
->setData( $this->getRequest()->getFiles() );
Which it does not. Looking through the framework source I confirmed that it shouldn't. So I was thinking about merging the file data into post data. Surely this cannot be the desired solution? It's not as if getPost() and getFiles() return easily mergeable arrays, they return Parameter objects.
Please note this is Zend Framework 2 specific.
Have you tried getFileInfo knowing now or paying mind to the fact that your using Zend. Typically on a per file basis $_FILE is an array based on the information of the file being uploaded. Filename, extension, etc.. Zends getFileInfo outputs that information in a similar fashion. Though I haven't played with it in sometime, its worth looking into
Example concept (more for multiple file uploads I know, but works with one, good concept to leave in tact just incase you wanna add a second or more files down the road)
$uploads = new Zend_File_Transfer_Adapter_Http();
$files = $uploads->getFileInfo();
foreach($files as $file => $fileInfo) {
if ($uploads->isUploaded($file)) {
if ($uploads->isValid($file)) {
if ($uploads->receive($file)) {
$info = $uploads->getFileInfo($file);
$tmp = $info[$file]['tmp_name'];
$data = file_get_contents($tmp);
// here $tmp is the location of the uploaded file on the server
// var_dump($info); to see all the fields you can use
}
}
}
}
After attempting to use Zend's file transfer adapter I went with a workaround in the controller. I think that the setData() in the form class should merge the items into the data instead of replacing them. (IMHO)
protected function getPostedData()
{
if ( is_null($this->postedData) )
{
$this->postedData = array_merge(
(array) $this->getRequest()->getPost(),
(array) $this->getRequest()->getFiles()
);
}
return $this->postedData;
}
I am using array_merge:
$form = $this->getForm('my_form');
$request = $this->getRequest();
if($request->isPost())
{
$file = $this->params()->fromFiles('name_of_file');
$form->setData(array_merge(
$request->getPost()->toArray(),
array('arquivo' => $file['name'])
));
if ($form->isValid()) {
// now i can validate the form field
I use variable variables like this article explains to create variables and then echo them as the values for each form entry.
example:
// create array of GET/POST variables then convert each variable to a local variable
$fields = array_keys($_REQUEST);
foreach ($fields as $field) {
$$field = $_REQUEST[$field];
}
Related
I'm new in Laravel, and I'm doing a project for my university, and I've two problems when I try to delete:
First error: When I try to delete the records, only the first one (with ID 1) doesn't delete from the table, but the other if deleted from the table and I've already tried some things to fix the error, but I couldn't:
Controller.php:
public function destroy_int($inst_id)
{
$inst = InstitucionEntidadInt::where('id', $inst_id);
$inst->delete();
return redirect('/activities/cons_instituciones_int');
}
Web.php:
Route::delete('/delete_inst_int/{inst_id}', [InstEntController::class, 'destroy_int'])
->name('institucion_int.destroy');
Second error: In some forms, it's necessary to upload and save files in a database (so I save just the name in the database and the files are saved in the public path). And some of this input files are multiple, so I save the names in the database like a JSON (using the json_encode method):
//This is the way that I save the files
$files = [];
if ($request->hasFile('inst_docsoporteNac')) {
foreach ($request->file('inst_docsoporteNac') as $file) {
$name = time() . "_" . $file->getClientOriginalName();
$file->move(public_path('files/institucionesNac'), $name);
$files[] = $name;
}
}
$instentNact->docSoportes = json_encode($files);
So, I'm trying to implement the delete method and I need to delete the files from both places (from the DB and the public path), I don't know how I can do it and I've already tried some "solutions" that I read from some forums (like this).
Controller.php:
public function destroy_nac($inst_id)
{
$inst = InstEntNac::where('id', $inst_id);
$files = InstEntNac::where('id', $inst_id)->get('docSoportes');
foreach (json_decode($files) as $file) {
Storage::delete(public_path('files/institucionesNac/' . $file));
}
$inst->delete();
return redirect('/activities/cons_instituciones_nac');
}
This is the way that I'm using, but I'm getting an error "Object of class stdClass could not be converted to string".
I'll appreciate your solutions for these errors.
For the first error, you can add error handling using this code:
InstitucionEntidadInt::findOrFail($inst_id)->delete();
For the second error, json_decode returns by default stdClass object instead of array. You can tell the function to return array type with second boolean parameter setting it to true:
json_decode($files, true)
https://www.php.net/manual/en/function.json-decode.php
I'm trying to pull data from an API and when the information is pulled into the fieldsets within my CMS, I want to ensure that whatever has been pulled from the API is validated properly with the rules I currently have set up within my CMS (eg. an image can be no bigger that 1920x1080 in dimensions).
Here is my code:
use App\Utilities\BardUtil;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Http;
use Statamic\Eloquent\Entries\EntryModel;
use Statamic\Events\EntrySaved;
use Statamic\Facades\Entry;
public function handle(EntrySaved $event): void
{
$entry = $event->entry->model();
if ($entry->collection != 'companies') {
return;
}
$data = collect($entry->data);
if (!isset($data['tickers'][0])) {
return;
}
$tickerId = $data['tickers'][0];
$ticker = EntryModel::find($tickerId);
if ($ticker && $ticker->title) {
$tickerTitle = $ticker->title;
$response = Http::get('apicallurlexample');
$fields = $event->entry->blueprint()->fields();
$items = $response->json('results.0');
$items['companyName'] = $items['exchangeName'];
$data = $data->merge($items);
$data['slug'] = $entry->slug;
$data['date'] = $entry->date;
$fields = $fields->addValues($data->toArray());
$collection = $event->entry->collection();
$site = $event->entry->site();
$rules = Entry::updateRules($collection, $site);
$replacements = [
'id' => $entry->id,
'collection' => $collection,
'site' => $site,
];
$fields
->validator()
->withRules($rules)
->withReplacements($replacements)
->validate();
$event->entry->data($fields->values()->all());
$event->entry->saveQuietly();
}
}
I think the issue lies within the following code:
$fields
->validator()
->withRules($rules)
->withReplacements($replacements)
->validate();
What's happening when I save the company details is that it displays all the validation errors, even if the data hasn't went against any of the rules. Any help would be appreciated and thanks in advance.
Sorry for the rather late reply but thought I'd update this thread anyways for others who find this later.
Statamic only uses the validation rules you define in your blueprint/fieldset when saving entries/terms/etc in the Control Panel.
When you save data manually (eg. via PHP or directly editing the Markdown files), no validation checks are ran on it. You'll need to manually do any kind of validation in your code, before the entry is created in Statamic.
I'm working on some project and I want to process requests from forms in several templates. The question is how is to invoke a proper function in the handling scrip. Although, I've been coding for a while, I still cant come up with anything better then using a variable in a hidden field:
if ($_POST['somehiddenfield'] == 1) {
some_function_1();//doesnt matter if its a function or a method
}
if ($_POST['somehiddenfield'] == 2) {
$mainclass->somemethod();
}
//goes on indefinitely
Also I want to keep everything in a single handler file, where my main class is invoked. So is there a more effective way than using if ... else?
I'd do the following:
still have a hidden field, but let it contain something like the form name
<input type="hidden" name="formName" value="post">
Then you can do something like that in the consuming php script:
<?php
// whatever class you use... this is just a simple dummy
class FormsProcessor {
public function post($params) {
echo "processing post form";
}
}
$formName = "post"; // would be $formName = filter_input(INPUT_POST, $_POST['formName'],FILTER_SANITIZE_STRING,FILTER_FLAG_STRIP_HIGH);
// BUT BE SURE TO SANITIZE THE INPUT!!!
$params = []; // dummy
$formsProcessor = new FormsProcessor();
// here's the trick.
$formsProcessor->{$formName}($params);
// to be even safer you could check first if this method_exists()
// and/or if it's in a list of allowed methods.
Be aware that there mustn't be any other methods in this class that the user shouldn't invoke. You could go around that by really compose the method name of two parts:
$methodName = $formName."Processor";
//....
$formsProcessor->{$methodName}();`
I would keep a key=>value array hardcoded with all the possible options. Pass the hidden input field, check if there are any intersections between your post values and the keys of the hardcoded options and call any matching values as functions.
$map = [
'yourHiddenField' => 'myFunctionName',
'anotherHiddenField' => 'myOtherFunctionName',
'yourOtherHiddenField' => 'yetAnotherfunctionName',
];
$intersection = array_intersect(array_keys($map), array_keys($_POST));
foreach ($intersection as $key) {
$this->{$map[$key])();
}
This code hasn't been tested.
EDIT:
Be careful with allowing ANY input to be ran without predefining which functions you should allow to be ran.
Example of how dangerous it could be even with sanitisation:
class Test {
public $i = 1;
function __construct(){
$this->i++;
}
}
$formVariable = '__construct';
$t = new Test();
$t->{$formVariable}();
echo $t->i;
I have this part of code:
if ($this->request->isPost() && $this->request->hasFiles()) {
$post = $this->request->getPost();
$file = $this->request->getUploadedFiles();
if ($form->isValid($post)) {
$form->bind((array) $this->request->getPost(), $entity);
$entity->save();
// Do some other stuff
}
}
Is there a way to pass $file to a Phalcon\Forms\Form object?
Or another practice to check if validity of a form that contains both files and text?
To validate Files you must use the FileValidator Class as this example:
$file = new FileValidator([
'maxSize' => '10M',
'messageSize' => _('Your file is huge. Max allowed is 10 Mb'),
'allowedTypes' => ["image/jpeg", "image/png"],
'messageType' => _('File type is not permitted. Try with JPG, GIF, etc..'),
'messageEmpty' => _('Cannot be empty. Please upload a file')
]);
$this->validator->add('filesInputName', $file);
$messages = $this->validator->validate($_FILES);
This is a really old question, but as it affects my current project I share my solution.
Normally, I'm using form classes and validation classes, too. For validating a file upload along with other POST data, I simply combine both payloads:
$entity = new MyCustomModel();
$merged = array_merge($_POST, $_FILES);
if ($form->isValid($merged, $entity)) {
// ...
}
In PHP I would like to be able to access PUT and DELETE vars globally similar to how GET and POST vars are accessed globally. I originally considered adding the data to $_PUT and $_DELETE respectively in the global namespace, but then I realized that the data for each request is stored in the message body so there's no way for there to be more than one dataset from a POST, PUT, or DELETE request.
Are there any side-effects of overwriting the $_POST variable?
i.e. str_parse( file_get_contents( 'php://input' ), $_POST );
Am I being silly, or is there a better way to access PUT and DELETE data?
Edit to clarify my thinking:
I am very well aware of the source of the data in $_POST, in fact i mentioned it earlier in my question. If a HTTP POST request is sent to the server the data is stored in php://input. If a HTTP PUT or DELETE request is sent to the server, the data is stored in the exact same place, meaning that $_POST will be empty (as no data was POSTed despite data being available.
A GET request, on the other hand, is passed via the query string. This allows simultaneous passing of $_POST and $_GET variables. It is not possible to simultaneously pass POST and PUT or DELETE variables.
If I overwrite $_POST from php://input on PUT and or DELETE requests, there is no data loss.
The alternative of adding:
global $_PUT;
global $_DELETE;
to the beginning of functions seems silly, as I'll only be able to use one at a time anyway.
My first question, which is the one I really want answered, is about what side-effects or issues exist in overwriting $_POST. I can't possibly be the first person to try something as silly as:
$_POST['foo'] = 'bar';
I'm just concerned that if I do anything similar that it might not be preserved across scopes.
You'll see this called "bad practice" all over the internet, but if you really get in to why it is "bad practice", well, the answers get fuzzy. The most concrete reason is the "hit by a bus" scenario so often bandied about - what if the project gets handed off to a new developer?
Hand wringing aside (you can leave comments, after all), there really isn't a compelling reason not to do it like this, but again, there isn't a compelling reason to do it, either. Why not put the values in a $_SESSION key if you want them global? Or make a global variable? Or make a static class to access the PUT/DELETE values through? With all the other optional approaches, I think that overwriting $_POST, while it won't make your server explode, is the most likely to cause you a headache down the road.
I threw this little static class together, you'll want to test this out before relying on it. Use:
//To check if this is a rest request:
Rest::isRest();
//To get a parameter from PUT
$put_var = Rest::put('keyname', false);
//To get a parameter from DELETE
$dele_var = Rest::delete('keyname', false);
class Rest {
static private $put = false;
static private $delete = false;
static private $is_rest = false;
function __construct() {
self::$is_rest = true;
switch ($_SERVER['REQUEST_METHOD']) {
case 'PUT':
parse_str(self::getVars(), self::$put);
break;
case 'DELETE':
parse_str(self::getVars(), self::$delete);
break;
default:
self::$is_rest = false;
}
}
private static function getVars() {
if (strlen(trim($vars = file_get_contents('php://input'))) === 0)
$vars = false;
return $vars;
}
public static function delete($key=false, $default=false) {
if (self::$is_rest !== true)
return $default;
if (is_array(self::$delete) && array_key_exists($key, self::$delete))
return self::$delete[$key];
return $default;
}
public static function put($key=false, $default=false) {
if (self::$is_rest !== true)
return $default;
if (is_array(self::$put) && array_key_exists($key, self::$put))
return self::$put[$key];
return $default;
}
public static function isRest() {
return self::$is_rest;
}
}
Leave Post and Get as it is. it shouldn't be modified as it's for reading purposes only. Create $_PUT and $_DELETE globals:
// globals
$_DELETE = array ();
$_PUT = array ();
switch ( $_SERVER['REQUEST_METHOD'] ) {
case !strcasecmp($_SERVER['REQUEST_METHOD'],'DELETE'):
parse_str( file_get_contents( 'php://input' ), $_DELETE );
break;
case !strcasecmp($_SERVER['REQUEST_METHOD'],'PUT'):
parse_str( file_get_contents( 'php://input' ), $_PUT );
break;
}
Not tested but you should get the idea. I was in the search for a rest framework myself some weeks ago and decided to go with python. Recess (http://www.recessframework.org/) sounds promising though
You shouldn't modify $_POST directly as this represents values coming from the client. Consider it read-only, and do any modifications in user-defined variable.
As a follow up regarding accessing PUT and DELETE data, currently there is no superglobal built in to PHP to access this data directly. As the data is file data, which can be rather large, the usefulness and efficiency of reading the entire file contents in a typical assignment statement $variable = $_PUT['file']; is questionable. Instead, it should be read in chunks. As such, the usage is consistent with reading from any other file input resource.
More on PUT here:
http://php.net/manual/en/features.file-upload.put-method.php
If you create a "request" object, then regardless whether the request comes over HTTP, command line, or through an HTML5 web socket, you will have a uniform way to access request data. You can then make the request object accessible in the global scope, or pass it as an argument to the required functions or methods.
Ideally you would store data that is independent of the request in static or global variables, e.g. settings that are "static" regardless of the request, and data specific to the request in a local variable or object, that could be used by your business logic. If you had a web socket server, for example, it would be easier to handle multiple request objects in a single PHP process. Here is an example that might help:
$headers = getallheaders();
$query = parse_str($_SERVER['QUERY_STRING']);
$data = file_get_contents('php://input');
if(strpos($headers['Content-Type'],'application/x-www-form-urlencoded') !== false)
{
$data = parse_str($data);
}
elseif(strpos($headers['Content-Type'],'application/json') !== false)
{
$data = json_decode($data);
}
elseif(strpos($headers['Content-Type'],'application/soap+xml') !== false)
{
$data = // parse soap
}
elseif(strpos($headers['Content-Type'],'application/xml') !== false)
{
$data = // parse xml
}
// else ...
$request = new Request($_SERVER['REQUEST_METHOD'],$data,$query);
// example business logic
$method = $request->get_request_method();
$obj = new BlogPost();
if($method == 'GET')
{
$obj->id($request->get_query('id'));
$obj->load();
}
elseif($method == 'PUT')
{
$obj->id($request->get_query('id'));
$obj->title($request->get_data('title'));
$obj->body($request->get_data('body'));
$obj->save();
}
elseif($method == 'POST')
{
$obj->title($request->get_data('title'));
$obj->body($request->get_data('body'));
$obj->save();
}
elseif($method == 'DELETE')
{
$obj->id($request->get_query('id'));
$obj->wipe();
}
Regardless of whether it is a PUT, POST, PATCH, or DELETE, there is only one body of data in the HTTP request, so your application does not need a complex $request object. The request object can make your controller (if you are using MVC) very simple.