ZendForm: move a picture into a folder - php

I'm working on a project with ZF2 and Zend Form. I'd like to add an avatar into a user profile.
The problem is that I only get the file name and save it in the DB. I would like to insert it into a folder so I'll be able to get it and display it. The rest of the form is working.
My guess is that I have to get information from $FILES, but I have no idea how to do this. I've read the documentation but can't see how to apply this to my project.
Thank you in advance!
Here's my Controller Action
public function signinAction()
{
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
$form = new SignupForm($this->em);
$model = new ViewModel(array("form" => $form));
$url = $this->url()->fromRoute("signin");
$prg = $this->prg($url, true);
if($prg instanceof \Zend\Http\PhpEnvironment\Response){
return $prg;
}
else if($prg === false){
return $model;
}
$user = new User();
$form->bind($user) ;
$form->setData($prg) ;
if($form->isValid()){
$bcrypt = new Bcrypt() ;
$pwd = $bcrypt->create($user->getPassword());
$user->setPassword($pwd);
$this->em->persist($user) ;
$this->em->flush() ;
return $this->redirect()->toRoute('login');
}
return $model ;
}
Here's my form file :
class SignupForm extends Form
{
private $em = null;
public function __construct($em = null) {
$this->em = $em;
parent::__construct('frm-signup');
$this->setAttribute('method', 'post');
$this->setHydrator(new DoctrineEntity($this->em, 'Application\Entity\User'));
//Other fields
...
//File
$this->add(array(
'type' => "File",
'name' => 'avatar',
'attributes' => array(
'value' => 'Avatar',
),
));
//Submit
...
}
}
And the form in my view :
$form = $this->form;
echo $this->form()->openTag($form);
//other formRow
echo $this->formFile($form->get('avatar'));
echo $this->formSubmit($form->get('submit'));
echo $this->form()->closeTag();

There are two things you could look at for getting your avatar to work:
Using the Gravatar view helper (uses gravatar.com service that automatically links images to email addresses)
documentation on using the gravatar service can be found here
Upload images yourself with the file upload classes that are shipped with ZF2:
form class for file upload can be found here
input filter class documentation can be found here
If you follow those docs you should be able to manage what you want.
Note: check especially the use of the Zend\Filter\File\RenameUpload filter in the example in the input filter documentation. This filter renames/moves the uploaded avatar file to the desired location.

Related

Unable to update file details entries in mysql db using Laminas and Doctrine

I'm able to add file details in database but not able to update it.
I am able to add file details entries, but when i try to update only the file that i am updating is moved to the storage folder. My update manager doesn't show any errors and doesn't update the file details in database.
this is my file form
protected function addElements()
{
// Add "name" field
$this->add([
'type' => 'file',
'name' => 'image',
'attributes' => [
'id' => 'image'
],
'options' => [
'label' => 'ImageFile',
],
]);
// Add the Submit button
$this->add([
'type' => 'submit',
'name' => 'submit',
'attributes' => [
'value' => 'Add Image File',
'id' => 'submit',
],
]);
// Add the CSRF field
$this->add([
'type' => 'csrf',
'name' => 'csrf',
'options' => [
'csrf_options' => [
'timeout' => 600
]
],
]);
}
public function addInputFilter()
{
$inputFilter = new InputFilter\InputFilter();
// File Input
$fileInput = new InputFilter\FileInput('image');
$fileInput->setRequired(true);
$inputFilter->add($fileInput);
$this->setInputFilter($inputFilter);
}
}
this is the update image manager
public function updateImage($name, $size)
{
$images = new Images();
$images->setName($name);
$images->setSize($size);
// Apply changes to database.
$this->entityManager->flush();
}
and this is my controller
public function editAction()
{
$id = (int)$this->params()->fromRoute('id', -1);
if ($id<1) {
$this->getResponse()->setStatusCode(404);
return;
}
$image = $this->entityManager->getRepository(Images::class)
->find($id);
if ($image == null) {
$this->getResponse()->setStatusCode(404);
return;
}
// Create form
$form = new ImageUploadForm('update', $this->entityManager);
$request = $this->getRequest();
if ($this->getRequest()->isPost()) {
$data = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);
$form->setData($data);
if($form->isValid()) {
$data = $form->getData();
$imgtmp = $data["image"]["tmp_name"];
$name = $data["image"]["name"];
$size = $data["image"]["size"];
$filepath = $this->_dir.$name;
move_uploaded_file($imgtmp, $filepath);
$this->achimotaImagesManager->updateImage($name, $size);
var_dump($name, $size);
return $this->redirect()->toRoute('images', ['action'=>'index']);
}
}
return new ViewModel([
'form' => $form,
]);
}
Do not create a new object
If you update an Images entity (consider naming it Image if it is one image), you should not create a new one. Hand over the $image you need to update:
public function updateImage($image, $name, $size){
$image->setName($name);
$image->setSize($size);
...
}
Persist before flush
You need to persist the entity before you flush.
$this->entityManager->persist($image);
$this->entityManager->flush();
Organize the code nicer
Do not inject entity manager in your controller. Inject rather a service through a factory, which handles all features of your Image entity. (ImageService.php)
Do not inject entity manager into your ImageService neither. Create a ImageMapper service, inject that into your ImageService. Create all Doctrine-related features in this Mapper. This has this advantage: Doctrin specific functionality is only in your Mapper files. Should you need to use another solution to store data, you only need to replace the Mapper files, providing the Service with the same interface.
Controller
public function editAction()
{
...
$this->serviceImage->update($image,$name,$size);
...
}
Service - ImageService.php
public function update($image,$name,$size)
{
$image->setName($name);
$image->setSize($size);
$this->mapperImage->save($image);
}
Mapper - ImageMapper.php
public function save($image)
{
$this->managerEntity->persist($image);
$this->managerEntity->flush();
}
Consider adding rich comments and typehints to the arguments and return value of the functions.
Moreover
The form should not be created in your controller. Put that code in your ImageService too. And consider inject form into the service. (Make sure you define the form for the factory in the getFormElementConfig()! This is more advance stuff, if you do not test with phpunit, you might not bother creating form as a service, hovever it leads to a very organized codebase.)
var_dump($name, $size) has no place in your controller. (If this is for debug purposes, it is OK, but use rather something like XDebug with a compatible IDE - PHPStorm is far the best one.)
This line is not so easy to understand: $filepath = $this->_dir.$name; Maybe:
$filePath = _dir . $name;
Naming convention: look for camelCase.

Zend Form : Call to undefined method Zend\InputFilter\InputFilter::getFilterChain()

I'm trying to upload an image with Zend Form. As we need it to move the image, I want to add a filter to do the job. But I can't use the getInputFilterChain(), I keep having this fatal error : Call to undefined method Zend\InputFilter\InputFilter::getFilterChain(). What am I missing here ?
I'm able to get the file information in $prg array. And as I looked on https://framework.zend.com/manual/2.4/en/modules/zend.mvc.plugins.html, this method is supposed to exist here, no ?
And I get the same error if I use this in my Form.php file.
Thank you in advance for your time!
My controller action :
public function signinAction()
{
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
$form = new SignupForm($this->em);
$form->getInputFilter()->getFilterChain()->attach(
new Zend\Filter\File\RenameUpload(array(
'target' => './data/tmpuploads/file',
'randomize' => true,
))
);
$model = new ViewModel(array("form" => $form));
$url = $this->url()->fromRoute("signin");
$prg = $this->fileprg($form, $url, true);
if($prg instanceof \Zend\Http\PhpEnvironment\Response){
return $prg;
}
else if($prg === false){
return $model;
}
//other stuff
...
}
You need to get the Input instance from the InputFilter first and then you can get the filter-chain from the input:
$inputName = 'file'; // name of the input you want to work with
$input = $form->getInputFilter()->get($inputName);
$filterChain = $input->getFilterChain();

CakePHP 3+ issue with DomPdf

I currently have a function in a project controller class that I am calling to export a specific project to a pdf. I am running into problems when I try to pass that single project page that I am pulling from. If I call function from my view and pass in a string of valid html from my export() function it will create a pdf correctly. I am just wondering how I can get it from that ctp template to my controller to be created as a pdf. Thanks.
In my ProjectsController.php
public function view($id)
{
$creator = $this->Auth->user();
$project = $this->Projects->get($id, [
'contain' => [
'Countries', 'Languages', 'Tags',
'ProjectsLanguages', 'Students'
]
]);
$languages = $this->__getLanguageReqs($id);
$tags = $this->__getTagReqs($id);
$projSupervisors = $this->__getSupervisorsProjects($id);
$this->set('locations',$this->__getLocations($id,"project"));
$this->set('projSupervisors',$projSupervisors);
if($creator['role_id'] == 2){
$this->set('is_owner',in_array($creator['id'],array_keys($projSupervisors)));
}
else{
$this->set('is_owner', false);
}
$this->set('languages',$languages);
$this->set('tags',$tags);
$this->set('project', $project);
$this->set('_serialize', ['project']);
}
public function export($id = null) {
$dompdf = new Dompdf();
$dompdf->loadHtmlFile('/projects/view/' . $id);
$dompdf->render();
$dompdf->output();
$dompdf->stream('project');
}
In my view.ctp
<button class = 'project_edit' onclick = "location.href='/projects/export/<?= h($project->id) ?>'">Export this Project</button>
Update
I got it figured out. Configured a new .ctp with the same information from my view.ctp and called an export there with the populated data in a php script at the end of my file.
You can use a plugin like cakephp-dompdf, your code will be cleaner.

Yii cant validate file input

I'm currently doing a project in PHP Yii Framework. I have a form which requires the user to upload a file. During the registration, user uploaded the file, however, when user submits the form, the form is always detected blank on the file input, it's like as if there is no attachment on the form. below is the code:
Model - CandidateResume:
return array(
array('resume_file','file','types'=>'doc,docx,pdf', 'allowEmpty'=>true, 'safe'=>true, 'on'=>'register'),
);
Model - Candidate:
return array(
array('can_email,name,repeat_can_email, can_password,repeat_can_password','required', 'on'=>'simplereg'),
);
View:
$form = $this->beginWidget('bootstrap.widgets.TbActiveForm',array(
'id'=>'candidate-form',
'enableAjaxValidation'=>true,
'type'=>'horizontal',
'htmlOptions' => array(
'enctype' => 'multipart/form-data',
'autocomplete'=>'off', //turn off auto complete in FF
)
));
echo $form->textFieldRow($model,'can_email',array('class'=>'span5','maxlength'=>100));
echo $form->textFieldRow($model,'repeat_can_email',array('class'=>'span5','maxlength'=>100));
echo $form->passwordFieldRow($model,'can_password',array('class'=>'span5','maxlength'=>100));
echo $form->passwordFieldRow($model,'repeat_can_password',array('class'=>'span5','maxlength'=>100));
echo $form->fileFieldRow($resume,'resume_file', array('id'=>'resume_file'));
$this->endWidget();
Controller - Candidate:
public function actionCreate()
{
$model = new Candidate();
$model->setScenario('simplereg');
$resume = new CandidateResume();
$resume->setScenario('register');
// Uncomment the following line if AJAX validation is needed
//$this->performAjaxValidation($model);
if(isset($_POST['Candidate'], $_POST['CandidateResume']))
{
$_POST['CandidateResume']['resume_file'] = $resume->resume_file;
$model->attributes = $_POST['Candidate'];
$resume->attributes = $_POST['CandidateResume'];
$uploadedFile = CUploadedFile::getInstance($resume,'resume_file');
if($resume->validate() && $model->validate())
{
$model->save();
if(!empty($uploadedFile)) // check if uploaded file is set or not
{
$saved = $uploadedFile->saveAs(Yii::app()->params['RESUME_PATH'].$model->can_id.'_'.$uploadedFile->getName());
$resume->resume_file = Yii::app()->params['RESUME_DIR'].$model->can_id.'_'.$uploadedFile->getName();
$resume->resume_send_ip = Yii::app()->request->userHostAddress;
}
$resume->save();
}
}
$this->render('create',array('model'=>$model, 'resume'=>$resume));
}
If I remove the validation on the controller:
if($resume->validate() && $model->validate())
The form data can be saved and attachment is placed properly in the folder. However, I need to do the validation for the form. Therefore I cant skip this part.
Is there anything that I missed out? I have checked many times and do researches for the solutions. All provides the similar solutions, therefore I can't figure out the things. Can anyone help me? Thank you in advance.
You don't set the resume_file attribute. It cames from $_FILES not from $_POST
$resume->attributes = $_POST['CandidateResume'];
$uploadedFile = CUploadedFile::getInstance($resume,'resume_file');
$resume->resume_file = $uploadedFile; //add this line

Zend Framework 2: Extend ZfcUser with own fields

I am working on my first Zend Framework 2 Project. I needed a User Module and integrated ZfcUser for this. Because I have a slight difference in my User Table, I had to use my own User Entity and User Mapper. I created a new Module called ZfcUserExtension.
I then copied a lot of files from the original ZfcUSer Module like:
Entity/User.php
Entity/UserInterface.php
Factory/Entity/IndexControllerFactory.php
Factory/Mapper/UserHydratorFactory.php
Mapper/Exeption/ExceptionInterface
Mapper/Exeption/InvalidArgumentException.php
Mapper/Exeption/RuntimeException.php Mapper/HydratorInterface.php
Mapper/User.php Mapper/UserHydrator.php Mapper/UserHydrator.php
Mapper/UserInterface.php
In zfcuser.global.php I set the user_entity_class to use my own Entity.
'user_entity_class' => 'ZfcUserExtension\Entity\User',
In the module.config.php from the ZfcUserExtension I add the below to make sure that I use my own User Mapper and UserHydrator. The reason for that was that I use "id" as a Primary Key in my User table instead of "user_id", so I had to make sure that this gets overwritten as well.
<?php
return array(
'controllers' => array(
'factories' => array(
'ZfcUserExtension\Controller\Index' => function(Zend\Mvc \Controller\ControllerManager $cm) {
$sm = $cm->getServiceLocator();
return new \ZfcUserExtension\Controller\IndexController(
$sm->get("doctrine.entitymanager.orm_default")
);
}
),
),
'service_manager' => array(
'factories' => array(
'zfcuser_user_mapper' => function ($sm) {
$options = $sm->get('zfcuser_module_options');
$mapper = new \ZfcUserExtension\Mapper\User();
// No db adapter present add below line
$mapper->setDbAdapter($sm->get('zfcuser_zend_db_adapter'));
$entityClass = $options->getUserEntityClass();
// No entity prototype set add below line
$mapper->setEntityPrototype(new $entityClass);
$mapper->setHydrator($sm->get('zfcuser_user_hydrator'));
$mapper->setTableName($options->getTableName());
return $mapper;
},
// 'zfcuserextension_change_password_form' => 'ZfcUserExtension\Factory\Form\ChangePhoneFormFactory',
),
),
I finally got all this to work, till I now run into another problem. I want some additional fields for the User like Phone Number. How would I approach this? I know there are some ideas on the Internet, but I am mainly interested to know how I would actually offer the option to have a "Change Phone" Form. I have created a Form, similar to the "Change Password and "Change Email". I have then created a IndexController.php in my ZfcUSerExtension, again followed the set-up of the UserController from the ZfcUser Module
class IndexController extends AbstractActionController {
const ROUTE_LOGIN = 'zfcuser/login';
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $em;
public function __construct(\Doctrine\ORM\EntityManager $em)
{
$this->em = $em;
}
/**
* #var Form
*/
protected $changeEmailForm;
public function indexAction() {
if (!$this->zfcUserAuthentication()->hasIdentity()) {
return $this->redirect()->toRoute(static::ROUTE_LOGIN);
}
return new ViewModel();
}
public function changephoneAction() {
// if the user isn't logged in, we can't change phone
if (!$this->zfcUserAuthentication()->hasIdentity()) {
return $this->redirect()->toRoute(static::ROUTE_LOGIN);
}
$form = $this->getChangePhoneForm();
$request = $this->getRequest();
$request->getPost()->set('PrevPhone', $this->getUserService()->getAuthService()->getIdentity()->getPrevPhone());
return array(
'status' => false,
'changePhoneForm' => $form,
);
$fm = $this->flashMessenger()->setNamespace('change-phone')->getMessages();
if (isset($fm[0])) {
$status = $fm[0];
} else {
$status = null;
}
$prg = $this->prg(static::ROUTE_LOGIN);
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
return array(
'status' => $status,
'changePhoneForm' => $form,
);
}
$form->setData($prg);
if (!$form->isValid()) {
return array(
'status' => false,
'changePhoneForm' => $form,
);
}
$change = $this->getUserService()->changeEmail($prg);
if (!$change) {
$this->flashMessenger()->setNamespace('change-email')->addMessage(false);
return array(
'status' => false,
'changeEmailForm' => $form,
);
}
$this->flashMessenger()->setNamespace('change-email')->addMessage(true);
return $this->redirect()->toRoute(static::ROUTE_CHANGEEMAIL);
}
public function getChangePhoneForm()
{
$sl = $this->getServiceLocator();
$this->setChangePhoneForm($sl->get('zfcuserextension_change_phone_form'));
return $this->changePhoneForm;
}
public function setChangePhoneForm($changePhoneForm)
{
$this->changePhoneForm = $changePhoneForm;
return $this;
}
I now noticed that I will face a problem with the User Service Service/User.php. The Service offers a changePassword() and changeEmail() Method. I now thought that I need to copy this file into my own Modules. Am I right that if I extend the User Service from ZfcUser then the Methods changePassword() and changeEmail() will still be available, so I would delete it from the just copied file and just add changePhone()?
And if I am right with my thoughts, the User Service currently starts like this:
class User extends EventProvider implements ServiceManagerAwareInterface
How would I have to change it that I extend the original User Service? I hope somebody can help, I am still rather confused with all this. Thanky you very much in advance.
There are two possible methods:
Build custom classes extending ZfcUser's entity, form and input filter and add your custom fields. In the ZfcUser configuration change aliases or override factories to ensure your custom classes are instantiated rather than the built in ones.
If you are OK with having the custom profile fields stored and accessed separately from the ZfcUser user entity, check out my module on GitHub: LdcUserProfile. It provides a profile system for ZfcUser but also makes it easy to add your own custom profile fieldsets linked to a user.

Categories