How can I get the javascript code generated wehen using ZendX_JQuery_Form_Element_AutoComplete
class?
$item = new ZendX_JQuery_Form_Element_AutoComplete('item', array(
'label' => 'Item' ,
'id' => 'auto-item'));
$item->setJQueryParams(array(
'source' => $some URL ,
'minLength' => '1'));
The javascript generated by the viewHelper will be :
$("#auto-item").autocomplete({"source":"some URL","minLength":"1"});
the last line is the target
Say you wanted to look-up a customer. Your source endpoint could be an action on a controller perhaps dedicated to serving autocomplete data.
Main thing to be aware of is format of the expected data:
[{"id":123,"label":"Some Customer","value":"Some Customer (ID#123)"},...
The 'label' and 'value' give you a chance to show alternate strings for the drop down list and what is ultimately selected.
Here is an example.
class Service_AutocompleteController extends Zend_Controller_Action
{
/**
* The query / q for the auto completion
*
* #var string
*/
protected $_term;
/**
* #see Zend_Controller_Action::init()
*/
public function init()
{
$this->_term = (isset($this->_request->term)) ? $this->_request->term : null;
}
/**
* Serve up JSON response for use by jQuery Autocomplete
*/
public function customerLookupAction()
{
// Disable the main layout renderer
$this->_helper->layout->disableLayout();
// Do not even attempt to render a view
$this->_helper->viewRenderer->setNoRender(true);
$query = "
SELECT TOP 50
c.id AS id,
c.name + '(ID#' + c.id + ')' AS label,
c.name AS value
FROM customer c
WHERE $label LIKE ?
ORDER BY c.name";
$db = Zend_Registry::get(SOME_DB_ADAPTER);
$results = $db->fetchAll($query, array('%' . $this->_term . '%'));
echo Zend_Json::encode($results);
}
}
Sometimes people take this a little further and have a view helper to send back the JSON, you could remove some of the repeated code in the controller actions that way.
Juste put this in your view file :
$this->jQuery()->getOnLoadActions()
it returns the javaScript generated by the jQuery helper
Related
Im trying to create an impersonate operation within my user controller, I have been following this guide..
impersonate for backpack
The setupImpersonateDefaults function gets called ok but i get a 404 error, after some testing i figured out the setupImpersonateRoutes is not getting triggered
Any ideas on why?
<?php
namespace App\Http\Controllers\Admin\Operations;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
use Illuminate\Support\Facades\Route;
use Session;
use Alert;
trait ImpersonateOperation
{
/**
* Define which routes are needed for this operation.
*
* #param string $segment Name of the current entity (singular). Used as first URL segment.
* #param string $routeName Prefix of the route name.
* #param string $controller Name of the current CrudController.
*/
protected function setupImpersonateRoutes($segment, $routeName, $controller)
{
Route::get($segment.'/{id}/impersonate', [
'as' => $routeName.'.impersonate',
'uses' => $controller.'#impersonate',
'operation' => 'impersonate',
]);
}
/**
* Add the default settings, buttons, etc that this operation needs.
*/
protected function setupImpersonateDefaults()
{
CRUD::allowAccess('impersonate');
CRUD::operation('impersonate', function () {
CRUD::loadDefaultOperationSettingsFromConfig();
});
CRUD::operation('list', function () {
// CRUD::addButton('top', 'impersonate', 'view', 'crud::buttons.impersonate');
CRUD::addButton('line', 'impersonate', 'view', 'crud::buttons.impersonate');
});
}
/**
* Show the view for performing the operation.
*
* #return Response
*/
public function impersonate()
{
CRUD::hasAccessOrFail('impersonate');
// prepare the fields you need to show
$this->data['crud'] = $this->crud;
$this->data['title'] = CRUD::getTitle() ?? 'Impersonate '.$this->crud->entity_name;
$entry = $this->crud->getCurrentEntry();
backpack_user()->setImpersonating($entry->id);
Alert::success('Impersonating '.$entry->name.' (id '.$entry->id.').')->flash();
// load the view
return redirect('dashboard');
// load the view
//return view('crud::operations.impersonate', $this->data);
}
}
Have tried following the guides and the routes are not getting added.
for anyone else looking at this, you need to call the route from the \routes\backpack\custom.php file, if its not called from this file it wont trigger the setupXXXRoute function
One of the official Backpack team members has created an add-on for impersonating users. You can use his add-on or get inspiration from it:
https://github.com/maurohmartinez/impersonate-users-backpack-laravel
I'm going to write REST API for my project. I'm using symfony 4. I saw several examples, but non of them fit me.
Validation with Form object. It doesn't work for me, because it's API, there are no forms. I don't want to write dummy classes just to support this functionality.
On this page https://symfony.com/doc/current/validation.html they suggest 4 ways: Annotation, yml, xml, php. This solution doesn't fit me because this validation is related to the entity, API - is much mode wider: it has limit, offset, filters and other fields, that doesn't belong to an entity.
So, I think I need to write validator which has an array of constraints for all possible fields. I just don't know what is the best way to present this. Have you ever seen something similar?
P.S. Before writing this post I used stackoverflow search. I didn't find useful answers.
Looking at your example (example.com/api/categories?limit=20&offset=300&filter=something) I guess your action would look something like this:
public function getCategories(?int $limit, ?int $offset, ?string $filter)
{
//...
}
Collection validation
You can define your constraints as an array (and later abstract it away into its own class), and pass it as the second argument to your validator.
$constraint = new Assert\Collection([
'limit' => [
new Assert\Range(['min' => 0, 'max' => 999]),
new Assert\DivisibleBy(0.5)
],
'offset' => new Assert\Range(['min' => 0, 'max' => 999]),
'filter' => new Assert\Regex("/^\w+/")
]);
$validationResult = $this->validator->validate(
['limit' => $limit, 'offset' => $offset, 'filter' => $filter],
$constraint
);
Documentation link.
Validate one by one
Pass the constraint to the validator as second argument, for every parameter you want to validate.
$offsetValidationResult = $this->validator->validate(
$offset,
new Assert\Range(['min' => 0, 'max' => 999])
);
//...
Documentation link.
Object validation
Create a class with the 3 fields in it.
class FilterParameters
{
public function __construct($limit, $offset, $filter)
{
$this->limit = $limit;
$this->offset = $offset;
$this->filter = $filter;
}
// No getters/setters for brevity
/**
* #Assert\DivisibleBy(0.25)
*/
public $limit;
/**
* #Assert\Range(min = 0, max = 999)
*/
public $offset;
/**
* #Assert\Regex("/^\w+/")
*/
public $filter;
}
Instantiate and validate it.
$validationResult = $this->validator->validate(
new FilterParameters($limit, $offset, $filter)
);
Documentation link.
I think to use forms as usual is the very clean and nice.
https://codereviewvideos.com/course/beginners-guide-back-end-json-api-front-end-2018/video/validating-json-data-symfony
I choose this api, because it was the fastest in my tests.
You do not have to buy the course (but you might if you like the code), just follow the "raw symfony 4" articles in this series (you also dont need the behat part)
"Limit", "offset" and "filter" functionality belongs to your repositories. Same way as you pass the id here to the repository
/**
* Class AlbumController
* #package App\Controller
*/
class AlbumController extends AbstractController
{
// ....
/**
* #Route(
* path = "/api/album/{id}",
* name = "get_album",
* methods = {"GET"},
* requirements = {"id"="\d+"}
* )
* #param int $id
*
* #return JsonResponse
*/
public function get($id)
{
return new JsonResponse($this->findAlbumById($id), JsonResponse::HTTP_OK);
}
/**
* #param $id
*
* #return Album|null
* #throws NotFoundHttpException
*/
private function findAlbumById($id)
{
$album = $this->albumRepository->find($id);
if ($album === null) {
throw new NotFoundHttpException();
}
return $album;
}
Scenario:
I have following model:
ContactPerson has a relation to FrontendUser and is the owning side of the relation. Now I have following problem:
I am activating/deactivating the FrontendUsers in a task, based on the ContactPersons which are active. When the FrontendUser is disabled or deleted the result of contactPerson->getFrontendUser() is null, even if both repositories ignoreEnableFields:
/** #var Typo3QuerySettings $querySettings */
$querySettings = $this->objectManager->get(Typo3QuerySettings::class);
$querySettings->setIgnoreEnableFields(true);
$querySettings->setRespectStoragePage(false);
$this->frontendUserRepository->setDefaultQuerySettings($querySettings);
$debugContactPerson = $this->contactPersonRepository->findOneByContactPersonIdAndIncludeDeletedAndHidden('634');
$debugFrontendUser = $this->frontendUserRepository->findOneByUid(7);
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump(
array(
'$debugContactPerson' => $debugContactPerson,
'$debugFrontendUser' => $debugFrontendUser,
)
);
Result:
P.s.: $this->frontendUserRepository->findByUid(7); also doesn't work because it isn't using the query, but persistenceManager->getObjectByIdentifier(... which is of course ignoring the query-settings.
The problem is, in my real code I can't use findOneByUid(), because I can't get the integer-Value(uid) in the frontend_user field of the contact_person.
Any way to solve this without using a raw query to get the contact_person-row?
My (yes raw query) Solution:
Because I didn't want to write an own QueryFactory and I didn't want to add a redundant field to my contactPerson I solved it now with a raw statement. Maybe it can help someone with the same problem:
class FrontendUserRepository extends \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository
{
/**
* #param \Vendor\ExtKey\Domain\Model\ContactPerson $contactPerson
* #return Object
*/
public function findByContactPersonByRawQuery(ContactPerson $contactPerson){
$query = $this->createQuery();
$query->statement(
"SELECT fe_users.* FROM fe_users" .
" LEFT JOIN tx_extkey_domain_model_contactperson contact_person ON contact_person.frontend_user = fe_users.uid" .
" WHERE contact_person.uid = " . $contactPerson->getUid()
);
return $query->execute()->getFirst();
}
}
Invoking repository directly
There are two aspects for the enable fields of table fe_users:
$querySettings->setIgnoreEnableFields(true);
$querySettings->setEnableFieldsToBeIgnored(['disable']);
Have a look to some overview in the wiki page - it says 6.2, but it's still valid in most parts for 7.6 and 8 as well. However, this only works if the repository is invoked directly, but not if an entity is retrieved as part of another entity - in this case the repository is not used for nested entities.
Modify query settings for nested entities
Nested entities are retrieved implicitly - this happens in DataMapper::getPreparedQuery(DomainObjectInterface $parentObject, $propertyName). To adjust query settings for child entities, the QueryFactoryInterface implementation has to be overloaded.
Register an alternative implementation in ext_localconf.php (replace \Vendor\ExtensionName\Persistence\Generic\QueryFactory with the real class name of your extension):
$extbaseObjectContainer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
\TYPO3\CMS\Extbase\Object\Container\Container::class
);
$extbaseObjectContainer->registerImplementation(
\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface::class,
\Vendor\ExtensionName\Persistence\Generic\QueryFactory::class
);
With new Typo3 versions (v8+), the registerImplementation method no longer works for QueryFactory. Instead, a XCLASS must be used to overwrite/extend the class:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory::class] = [
'className' => \Vendor\ExtensionName\Persistence\Generic\QueryFactory::class,
];
Then inside the implementation:
<?php
namespace \Vendor\ExtensionName\Persistence\Generic;
use TYPO3\CMS\Extbase\Domain\Model\FrontendUser;
class QueryFactory extends \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory {
public function create($className) {
$query = parent::create($className);
if (is_a($className, FrontendUser::class, true)) {
// #todo Find a way to configure that more generic
$querySettings = $query->getQuerySettings();
$querySettings->setIgnoreEnableFields(true);
// ... whatever you need to adjust in addition ...
}
return $query;
}
}
My solution of this problem was to disable the "enablecolumns" in TCA definitions and deal this in the repository myself.
Here an example of findAll method:
public function findAll($ignoreEnableFields = false) {
$query = $this->createQuery();
if (!$ignoreEnableFields) {
$currTime = time();
$query->matching(
$query->logicalAnd(
$query->equals("hidden", 0),
$query->logicalOr(
$query->equals("starttime", 0),
$query->lessThanOrEqual("starttime", $currTime)
),
$query->logicalOr(
$query->equals("endtime", 0),
$query->greaterThanOrEqual("endtime", $currTime)
)
)
);
}
return $query->execute();
}
I have created a CRUD with Symfony 3 that allows me to create different missions with a few specificities. I want to to create a function that allows someone with a specific role to change a mission's status just by clicking a button, that would be shown in the view like this
{{form_start(missionInProgress) }}
<input type="submit" value="Submit" />
{{form_end(missionInProgress) }}
Since I'm a real newbie and I can't find concrete example on Google, I tried a lot of things, but none worked so far. I tried to create a public function that would modify the mission's status when someone clicks on the input button
public function that updates the mission's status:
/**
* #Route("/{id}/encours", name="mission_encours")
* #Security has_role('ROLE_RECRUTEUR')
* #Method("POST")
*/
public function enCoursAction(Request $request, Mission $mission){
$form = $this->missionInProgress($mission);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$mission->setStatut("En cours");
$em->persist($mission);
}
}
And I also tried to create a private function like the one that allows a mission to be deleted from anywhere.
**Private function that calls the public function: **
/**
* #param Mission $mission
* #Security has_role('ROLE_RECRUTEUR')
* #return \Symfony\Component\Form\Form The form
*/
private function missionInProgress(Mission $mission){
$this->createFormBuilder()
->setAction($this->generateUrl('mission_encours', array('id' => $mission->getId())))
->setMethod('POST')
->getForm();
}
Following the "createDeleteForm" example, I implemented it in the showAction as follow:
/**
* Finds and displays a Mission entity.
*
* #Route("/{id}", name="mission_show")
* #Method("GET")
*/
public function showAction(Mission $mission)
{
$deleteForm = $this->createDeleteForm($mission);
$enCours = $this->missionInProgress($mission); /* There */
return $this->render('mission/show.html.twig', array(
'mission' => $mission,
'delete_form' => $deleteForm->createView(),
'missionInProgress' => $enCours->createView(), /* And there */
));
}
But when I try to see the result, I get the following error:
Error: Call to a member function createView() on null
Obviously nothing gets inside missionInProgress(), but I can't figure why and how to make this damn thing work. I also don't think that everything I did was necessary, but I thought that if I do this, I might increase my success chances...
Anyone has an idea ?
Thank you in advance
Try to add returnin your missionInProgress() method
I am currently developing a Website in which user may buy gift cards. I am using a three step form using the CraueFormFlow bundle and everything is concerning the steps. I am able to validate every simple Assert (like not blank, email, repeated fields, etc) but I am facing the situation where, user may select 0 gift cards and proceed to the next page.
The users may choose the quantity of giftcards they want to buy using two separate : one for 25$ gift cards and one for 50$ gift cards. So I can't just put a validator saying "value 0 is not allowed". The validator must prevent a user from leaving the quantity "0" in both amount (25$ and 50$).
Does anyone know how to make a custom validation looking for the values in two fields?
Thanks in advance!
You have many solutions for this.
The easiest one is to add a Callback constraint to your model class.
Another way to do it would be to create your custom constraint and its associated validator. You have a cookbook explaining how to create a custom validation constrain.
This is the best approach to do it.
As your constraint does not apply to a property but to a class, you must specify it overriding the the ->getTargets() method of your constraint class:
class MyConstraint extends Constraint
{
// ...
public function getTargets()
{
return Constraint::CLASS_CONSTRAINT;
}
}
So the value passed as $value argument of the ->isValid() method will contain values of the whole class and not only of a single property.
When you don't have a data class attached to your form you can implement dependent constraints in forms like this:
$startRangeCallback = function ($object, ExecutionContextInterface $context) use ($form)
{
$data = $form->getData();
$rangeEnd = $data['range_end'];
if($object && $rangeEnd){
if ($object->getTimestamp() > $rangeEnd->getTimestamp()) {
$context->addViolation('Start date should be before end date!', array(), null);
}
}
};
$form->add('range_start', 'bootstrap_datepicker', array(
'format' => 'dd-MM-yyyy',
'required' => false,
'attr' => array('class' => "col-xs-2"),
'calendar_weeks' => true,
'clear_btn' => true,
'constraints' => array(
new Callback(array($startRangeCallback)),
)
)
);
$form->add('range_end', 'bootstrap_datepicker', array(
'format' => 'dd-MM-yyyy',
'required' => false,
'attr' => array('class' => "col-xs-2"),
'calendar_weeks' => true,
'clear_btn' => true,
)
);
This is how I've done this in my validation constraints, to check credit card validity with expiration month and year properties.
In this class, I check the value of expirationYear property and compare it with value of expirationMonth property got from contextObject.
/**
* Method to validate
*
* #param string $value Property value
* #param \Symfony\Component\Validator\Constraint $constraint All properties
*
* #return boolean
*/
public function validate($value, Constraint $constraint)
{
$date = getdate();
$year = (string) $date['year'];
$month = (string) $date['mon'];
$yearLastDigits = substr($year, 2);
$monthLastDigits = $month;
$otherFieldValue = $this->context->getRoot()->get('expirationMonth')->getData();
if (!empty($otherFieldValue) && ($value <= $yearLastDigits) &&
($otherFieldValue <= $monthLastDigits)) {
$this->context->addViolation(
$constraint->message,
array('%string%' => $value)
);
return false;
}
return true;
}
Of course, you have to authorize class and properties constraints in your getTargets method, form the main constraint file.
/**
* Get class constraints and properties
*
* #return array
*/
public function getTargets()
{
return array(self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT);
}
Further explanations and complete tutorial here: http://creativcoders.wordpress.com/2014/07/19/symfony2-two-fields-comparison-with-custom-validation-constraints/
Use Regular expression inorder to prevent Zero
In your Entity class write down the below override function , and specify your property which you need to validate.
The below example is for validating a pincode ,here in pincode field I admit only numbers 0-9 combinations upto 10 digits .
" ^\d+$ " this is the regular expression I used to prevent other characters.
For overriding this function you must include the below classes
use Symfony\Component\Validator\Mapping\ClassMetadata;// for overriding function loadValidatorMetadata()
use Symfony\Component\Validator\Constraints\NotBlank;// for notblank constrain
use Symfony\Component\Validator\Constraints\Email;//for email constrain
use Symfony\Component\Validator\Constraints\MinLength;// for minimum length
use Symfony\Component\Validator\Constraints\MaxLength; // for maximum length
use Symfony\Component\Validator\Constraints\Choice; // for choice fields
use Symfony\Component\Validator\Constraints\Regex; // for regular expression
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('pincode', new NotBlank(array('message' => 'Does not blank')));
$metadata->addPropertyConstraint('pincode', new Regex(array('pattern'=>'/^\d+$/','message' => 'must be number')));
$metadata->addPropertyConstraint('pincode', new MaxLength(array('limit'=>'6','message' => 'must maximum 6 digits')));
$metadata->addPropertyConstraint('pincode', new MinLength(array('limit'=>'6','message' => 'must minimum 6 digits')));
}
Not forget these all must
included in your Entity class
that you have to validate. So in your case use a proper regular expression which does not permit '0'.
Happy coding
I'd suggest using Expression constraint. This constraint can be applied on form field or (preferably) in entity:
/**
* #var int
* #Assert\Type(type="integer")
*/
private $amountGiftCards25;
/**
* #var int
* #Assert\Type(type="integer")
* #Assert\Expression(expression="this.getAmountGiftCards25() > 0 or value > 0", message="Please choose amount of gift cards.")
*/
private $amountGiftCards50;