Okay, so I have an entity with crud created right... and it gives me the .yml routing file for the entity...
in the yml file I have specified a route such as:
manager_agentview:
pattern: /manager/{id}/view
defaults: { _controller: "EcsCrmBundle:Management:agentview" }
This works perfectly... However, the contents of this page is a list...
My function, is like this:
public function agentviewAction($id, $start = null, $end = null) {
$em = $this->getDoctrine()->getEntityManager();
$request = $this->getRequest();
$today = time();
echo $end;
if ($end == null) {
if (date('l') == "Saturday") { $end = date("Y-m-d 23:59:59"); } else { $end = date("Y-m-d 23:59:59", strtotime('next saturday', $today)); }
}
if ($start == null) {
if (date('l') == "Sunday") { $start = date('Y-m-d 00:00:00'); } else { $start = date('Y-m-d 00:00:00', strtotime('last sunday', $today)); }
}
$entities = $em->getRepository('EcsCrmBundle:TimeClock');
$query = $entities->createQueryBuilder('t');
$query = $query->select('t')
->where('t.daydate BETWEEN :start AND :end')
->andwhere("t.noteBy = :id")
->orderBy("t.id", 'ASC')
->setParameter('start', $start)
->setParameter('end', $end)
->setParameter('id', $id)
->getQuery();
$entities = $query->getArrayResult();
$dateRangeForm = $this->createForm(new DateRangeType());
$query = $em->getRepository('EcsAgentManagerBundle:User')->find($id);
//return new Response('yep', 200);
return $this->render('EcsCrmBundle:TimeClock:view.html.twig', array('entity' =>$entities, 'user' => $query, 'start' => $start, 'end' => $end, 'form' => $dateRangeForm -> createView(),));
}
the dateRangeForm, simply creates 2 jquery datepicker boxes..
But, when I add in {start}/{end} to my route, it constantly tells me that it can't find the proper route.... Since I want the URL to stay the same (ultimately - without the dates being in the URL) -- I've got to figure out how to post the data to the same function without breaking the ability to just view it by just going to something like: site.dev/manager/12/view
If I understand your question correctly, you want to be able to have a single controller function that handles
/manager/1/view/start_date/end_date
and also handles
/manager/1/view
Correct?
There are two solutions to this. The first is to create an optional placeholder, which is described here: http://symfony.com/doc/current/book/routing.html. Basically, you can update your route to look like this:
manager_agentview:
pattern: /manager/{id}/view/{start}/{end}
defaults: { _controller: "EcsCrmBundle:Management:agentview", start: null, end: null}
The route will then still match /manager/1/view and will set start and end to NULL.
The second solution would be to create multiple routes that all point to the same controller, one version with the start and end dates and one version without them. The only reason I can think of that you might want to do this would be if you didn't want your route to match a URL that contained a start but no end, since the above solution would still match in that case.
Related
I am using symfony3 with window 7:
This method should work for both action update and add.But its behavior only insertion. while i am setting $id also.
/**
* #Route("entity/entity/{id}", name="entity_entity",defaults={"id" = 0})
*/
public function entityAction(Request $request,$id){
$action = false;
$arr_XYZ_data = array();
$arr_XYZ_prepare_data = array();
$form_title = 'Add New XYZ';
$obj_XYZ = new XYZ();
$form = $this->createForm(XYZType::class, $obj_XYZ);
if($id!=0){
$obj_repo = $this->getDoctrine()->getRepository('AppBundle:XYZ');
$arr_XYZ_data = $obj_repo->find($id);
if($arr_XYZ_data){
$action = true;
$form_title = 'Update XYZ';
$arr_XYZ_data = $obj_repo->findXYZById($id);
$arr_XYZ_prepare_data = $this->_prepareData($arr_XYZ_data);
}
}
$form->handleRequest($request);
if (($form->isSubmitted())) {
$obj_XYZ->setXYZId($id);
$str_hiddenfield_result = $form->get('extraformfield')->getData();
$arr_hiddenfield_result = explode('&',$str_hiddenfield_result);
$obj_XYZ->setDef($obj_XYZ->getDef()->getDefId());
$obj_XYZ->setAbc($arr_hiddenfield_result[3]);
$obj_XYZ->setAuthor(1); //ldap session value
$em = $this->getDoctrine()->getManager();
$em->persist($obj_XYZ);
$em->flush();
$this->addFlash('success', 'Your record has been added successfully!');
return $this->redirectToRoute('XYZ_index', array(), 301);
}
}
anyone can suggest me how can i achieve this ?
Some remarks:
Are you calling the right URL (with $id != 0)?
Check if the form that is submitted is valid before doing anything.
Why are you calling setId() on the entity? Doctrine will set the ID of a new and persisted entity.
Finally, you are using a 301 redirect, which is a Permanent Redirect. This means that the browser will redirect any request to entity/entity/{id} to whichever URL is generated by XYZ_index.
I would recommend the following things:
Use isValid instead of isSubmitted for the form (it might not be valid but you are persisting its data!).
Use the built-in Forms, which you can load with an entity (so that you do not have to process data fields yourself).
Return a $this -> redirectToRoute('...', array(...)) instead of a 301.
After a lot of R&D i got the solution:
use:
$em->merge($obj_XYZ) instead of $em->persist($obj_XYZ);
before
$em->flush();
I'm working on a symfony project entity with query builder. When I try to run this function I get this issue.
Invalid parameter number: number of bound variables does not match number of tokens
public function json_filterAllproductsAction() {
$search = "";
$category = 1;
//Combine tables and create the query with querybuilder
$em = $this->container->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
$qb->select('p')
->from('EagleAdminBundle:Products', 'p')
->orderBy('p.id', 'DESC');
if ($category != 0) {
$qb->where($qb->expr()->in('p.category', '?1'))
->setParameter(1, $category);
}
$qb->where('p.productTitle LIKE :title')
->setParameter('title', "$search%");
//convert to json using "JMSSerializerBundle"
$serializer = $this->container->get('serializer');
$jsonproducts = $serializer->serialize($qb->getQuery()->getResult(), 'json');
return new Response($jsonproducts);
}
I think error is in
$qb->where($qb->expr()->in('p.category', '?1'))
->setParameter(1, $category);
It would be great help someone can help me.
You have two issues here. The first is that your last where clause overwrites the first one. This can be fixed by using andWhere. The second is that your mixing named parameters (:title) with positional parameters (?1). Mixing is a no no. And you don't really need the expr object. Try:
$qb->select('product')
->from('EagleAdminBundle:Products', 'product')
->orderBy('product.id', 'DESC');
if ($category) {
$qb->andWhere('product.category IN (:category)');
$qb->setParameter('category', $category);
}
$qb->andWhere('product.productTitle LIKE :title');
$qb->setParameter('title', "$search%");
I'm using a search code and pagination code in my controller(s), it goes without saying it's bad coding habits by repeating code. That being said what is the best practice in Symfony2, to avoid repeating code in all my controllers?
And how do I access the code once it's been re-factored?
Controller
// Search code
$results = null;
$query = $request->query->get('q');
if (!empty($query)) {
$em = $this->getDoctrine()->getManager();
$results = $em->createQueryBuilder()
->from('AcmeDemoBundle:Blog', 'b')
->select('b')
->where('b.title LIKE :search')
->setParameter(':search', "%${query}%")
->getQuery()
->getResult();
}
// Pagination code
$page = $request->get('page');
$count_per_page = 5;
$total_count = $this->getTotalBlogs();
$total_pages = ceil($total_count/$count_per_page);
if (!is_numeric($page)) {
$page = 1;
} else {
$page = floor($page);
}
if ($total_count <= $count_per_page) {
$page = 1;
}
if (($page * $count_per_page) > $total_count) {
$page = $total_pages;
}
$offset = 0;
if ($page > 1) {
$offset = $count_per_page * ($page - 1);
}
$em = $this->getDoctrine()->getManager();
$blogQuery = $em->createQueryBuilder()
->select('b')
->from('AcmeDemoBundle:Blog', 'b')
->addOrderBy('b.created', 'DESC')
->setFirstResult($offset)
->setMaxResults($count_per_page);
$blogFinalQuery = $blogQuery->getQuery();
$blogPage = $blogFinalQuery->getArrayResult();
foreach ($blogPage as $blog) {
$blog_id = $blog['id'];
$commentRepository = $this->getDoctrine()
->getRepository('AcmeDemoBundle:Comment');
$comments[] = $commentRepository->findByBlog($blog_id);
}
// exit(\Doctrine\Common\Util\Debug::dump($comments));
return $this->render('AcmeDemoBundlBundle:Default:index.html.twig', array(
'blogPage' => $blogPage,
'total_pages' => $total_pages,
'current_page' => $page,
'comments' => $comments,
'query' => $query,
'results' => $results,
));
For a start, you can put all your custom queries in custom repository classes. I suspect that would cover all the re-use you need in this case.
For example, create a BlogRepository class in AcmeDemoBundle:Repository and annotate the Blog entity class as follows to define it's repository class:
/**
* #ORM\Entity(repositoryClass="Acme\DemoBundle\Repository\BlogRepository")
*/
Then add methods to the repository for each custom query that's needed, bearing in mind the way that repository methods are typically named. It looks as though the controller method getTotalBlogs() could also be a method on BogRepository e.g.:
public function findFiltered($page, $count_per_page = 5, $filterText = '')
{
$total_count = $this->findTotal();
// Code to initialise page and offset here
$queryBuilder = $this->createQueryBuilder('blog');
$queryBuilder->...
...
}
Note that the above method could be used to get all the blogs if no $filterText is passed in. You would just need something like this:
if (!empty($filterText))
{
queryBuilder->where('b.title LIKE :search')
->setParameter(':search', "%${query}%")
}
Then, a CommentRepository could be created with a method to find all the comments for a given set of blog(id)s. Note you could use a sql 'IN' clause to get all the comments using a single query:
$commentQuery = $em->createQueryBuilder()
->select('comment')
->from('AcmeDemoBundle:Comment', 'comment')
->where('comment.blog IN (:ids)')
->setParameter('ids', $blogIds);
In addition to custom repository classes I use manager services (e.g. BlogManager) to encapsulate business processes. My controllers mainly use the managers rather than using the repositories directly but it depends on the functionality.
I'm a little confused that you have an overall results query which only returns Blogs where the title is like the search text whilst the paged query returns (a page of) all blogs. That may just be a because your code is in progress?
I have a vocab category and four terms within it. what i want to do is if content is tagged with a termin in particular say "term1" to have the url generated as word1/[node:title] and for all the other tags just the standard url formatting.
If i wanted the term in the url obviously id use pattern replacement but i want another word to be used if a particular tag is used
I can't think of an easy plug-and-play way of achieving this. You may have to create your own token for the "Default path pattern" in Pathauto's URL alias settings:
/**
* Implementation of hook_token_info().
*/
function MODULE_token_info() {
$info['tokens']['node']['node-term-path'] = array(
'name' => t('Node path by term'),
'description' => t('The path to a node based on its taxonomy terms.'),
);
return $info;
}
/**
* Implementation of hook_tokens().
*/
function MODULE_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'node' && !empty($data['node'])) {
$node = $data['node'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'node-term-path':
$items = field_get_items('node', $node, 'TAXONOMY_FIELD_NAME');
foreach ($items as $item) {
$tids[] = $item['tid'];
}
if (in_array(TID_OF_TERM1, $tids)) {
// Path for nodes with term1
$replacements[$original] = 'word1/'. pathauto_cleanstring($node->title);
}
else {
// Path for other nodes
$replacements[$original] = 'content/'. pathauto_cleanstring($node->title);
}
break;
}
}
}
return $replacements;
}
Found a simple way actually to anyone who need a similar solution use the module Entity Reference.
http://drupal.org/project/entityreference
I just created a new field for the user account select entity reference then you can choose any entity within drupal to reference.
(ie so you can select a term/content/anything)
I have been going through the docs and source code looking for something without luck.
Is there a Drupal 6 hook that gets called after hook_search(), but before the $results gets handed off to the template system?
I need to do a fairly custom pruning and reordering of results that get returned. I could just reimplement hook_search(), but this seems like overkill.
Thanks.
There isn't; search_view() (which displays the results) calls search_data(), which invokes hook_search() then immediately themes the results. Re-implementing hook_search() is probably the most straightforward route.
With that said, you could instead implement hook_menu_alter() and have the search page call your custom function instead of calling search_view() (and subsequently calling search_data()). Something like:
function test_menu_alter(&$items) {
$items['search']['page callback'] = 'test_search_view';
foreach (module_implements('search') as $name) {
$items['search/' . $name . '/%menu_tail']['page callback'] = 'test_search_view';
}
}
// Note: identical to search_view except for --- CHANGED ---
function test_search_view($type = 'node') {
// Search form submits with POST but redirects to GET. This way we can keep
// the search query URL clean as a whistle:
// search/type/keyword+keyword
if (!isset($_POST['form_id'])) {
if ($type == '') {
// Note: search/node can not be a default tab because it would take on the
// path of its parent (search). It would prevent remembering keywords when
// switching tabs. This is why we drupal_goto to it from the parent instead.
drupal_goto('search/node');
}
$keys = search_get_keys();
// Only perform search if there is non-whitespace search term:
$results = '';
if (trim($keys)) {
// Log the search keys:
watchdog('search', '%keys (#type).', array('%keys' => $keys, '#type' => module_invoke($type, 'search', 'name')), WATCHDOG_NOTICE, l(t('results'), 'search/'. $type .'/'. $keys));
// Collect the search results:
// --- CHANGED ---
// $results = search_data($keys, $type);
// Instead of using search_data, use our own function
$results = test_search_data($keys, $type);
// --- END CHANGED ---
if ($results) {
$results = theme('box', t('Search results'), $results);
}
else {
$results = theme('box', t('Your search yielded no results'), search_help('search#noresults', drupal_help_arg()));
}
}
// Construct the search form.
$output = drupal_get_form('search_form', NULL, $keys, $type);
$output .= $results;
return $output;
}
return drupal_get_form('search_form', NULL, empty($keys) ? '' : $keys, $type);
}
// Note: identical to search_data() except for --- CHANGED ---
function test_search_data($keys = NULL, $type = 'node') {
if (isset($keys)) {
if (module_hook($type, 'search')) {
$results = module_invoke($type, 'search', 'search', $keys);
if (isset($results) && is_array($results) && count($results)) {
// --- CHANGED ---
// This dsm() is called immediately after hook_search() but before
// the results get themed. Put your code here.
dsm($results);
// --- END CHANGED ---
if (module_hook($type, 'search_page')) {
return module_invoke($type, 'search_page', $results);
}
else {
return theme('search_results', $results, $type);
}
}
}
}
}
You can use hook_search_page() to reorder or format the search result.
Hook search_execute allows you to modify the query in the way you needed. You can even fire new queries with custom sql, for example:
function mymodule_search_execute($keys = NULL, $conditions = NULL) {
// Do some query here.
$result = my_fancy_query();
// Results in a Drupal themed way for search.
$results[] = array(
'link' => (string) $result->U,
'title' => $title,
'snippet' => $snippet,
'keys' => check_plain($keys),
'extra' => array($extra),
'date' => NULL,
);