Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search within contenttype #1578

Closed
I-Valchev opened this issue Jun 29, 2020 · 9 comments
Closed

Search within contenttype #1578

I-Valchev opened this issue Jun 29, 2020 · 9 comments

Comments

@I-Valchev
Copy link
Member

If I am on a listing page, e.g. /blog I want to be able to add a search form that, on submit, goes to /blog?search=term.

On that listing page with query param search, I want the records variable to only include records matched against the search term.

Afaik, this is not (yet) implemented, right? @bobdenotter

@bobdenotter
Copy link
Member

This is something that can be done in twig only, on the frontend.. Do you think this is something that should be coe functionality, or a snippet or something elsewhere explaining how to do it?

@I-Valchev
Copy link
Member Author

I-Valchev commented Jun 29, 2020

Hmm, I think that depends on how easy it is to implement in twig. If it is several lines of straightforward code, I think snippet / docs how-to page is better... less noise in core.

If not, putting it in core (or extension?) is needed imho. It looks like a fairly common use case to be able to search for blog posts, news, events, etc. and be able to display them with the same listing template (rather than the generic search one).

Let's see if we can look it up tomorrow :-)

@I-Valchev
Copy link
Member Author

okay, we have a bolt.tips entry about that, using:

{% setcontent records = contenttype.slug where { 'anyField': '%' ~ searchTerm ~ '%' } %}

Documentation needs updating, do we even need pages/search handler in that case? @bobdenotter

@JTNMW
Copy link
Contributor

JTNMW commented Jun 30, 2020

i have just implemented this in my project with a slight change to the listingController and contentRepository ( both of which are in my project/src - not vendor/bolt/src). All params are passed and if search (anyfield) is given it performs a searchNaive() on the search term in all fields and taxonomies and returns records to the listing.twig.

It also allows specific fields and taxonomies to be queried in the url (using the changes in selectQuery.php i've just PR #1541 ) and returns results to the listing. functions including changes below:

listingController.php

public function listing(ContentRepository $contentRepository, Request $request, string $contentTypeSlug): Response
    {
        $contentType = ContentType::factory($contentTypeSlug, $this->config->get('contenttypes'));
        $page = (int) $request->query->get('page', 1);
        $amountPerPage = $contentType->get('listing_records');
         //get all query parameters
		$params = $request->query->all();
		
		
		//check if search is set and move to anyField param with like %...% appended.
		if( $request->query->get('search') !='') { $params['anyField'] = '%'.$request->query->get('search').'%';
            //clear search param 
            unset ($params ['search']);};
		
		
		//get published records only
		$params ['status'] = 'published';
		
		//set order defaults if not set in query
        $params ['order'] = $request->query->get('order', $contentType->get('order'));
      
		 if (! empty($params['anyField'])) {
             $contentTypes = $this->config->get('contenttypes')->where('searchable', true);
             
            $records = $contentRepository->searchNaive($params['anyField'], $page, $amountPerPage, $contentTypes);
        } else {
            // include all params as array
		$records = $this->query->getContent($contentTypeSlug, $params)
            ->setMaxPerPage($amountPerPage)
            ->setCurrentPage($page);
        }
		
		

        /*
        $order = $request->query->get('order', $contentType->get('order'));

        $records = $this->query->getContent($contentTypeSlug, [
            'status' => 'published',
            'order' => $order,
        ])
            ->setMaxPerPage($amountPerPage)
            ->setCurrentPage($page);*/

        $templates = $this->templateChooser->forListing($contentType);

        $twigVars = [
            'records' => $records,
            $contentType->getSlug() => $records,
            'contenttype' => $contentType,
        ];

        return $this->renderTemplate($templates, $twigVars);
    }
}

contentRepository.php

public function searchNaive(string $searchTerm, int $page, int $amountPerPage, Collection $contentTypes, bool $onlyPublished = true): Pagerfanta
    {
        // First, create a querybuilder to get the fields that match the Query
        $qb = $this->getQueryBuilder()
            ->select('partial content.{id}');

        $qb->addSelect('f')
            ->innerJoin('content.fields', 'f')
            ->innerJoin('f.translations', 't')
            ->andWhere($qb->expr()->like('LOWER(t.value)', 'LOWER(:search)'))
            ->setParameter('search', '%'.$searchTerm . '%')

            //ACNMW - added to incude taxonomies to be searched
            ->addSelect('tx')
            ->innerJoin('content.taxonomies', 'tx')
            ->orWhere($qb->expr()->like('tx.slug', ':slug'))
            ->setParameter('slug', '%' . $searchTerm . '%');

        // These are the ID's of content we need.
        $ids = array_column($qb->getQuery()->getArrayResult(), 'id');

        // Next, we'll get the full Content objects, based on ID's
        $qb = $this->getQueryBuilder()
            ->addSelect('a')
            ->innerJoin('content.author', 'a')
            ->orderBy('content.modifiedAt', 'DESC');

        if ($onlyPublished) {
            $qb->andWhere('content.status = :status')
                ->setParameter('status', Statuses::PUBLISHED);
        }

        $qb->andWhere('content.contentType IN (:cts)')
            ->setParameter('cts', $contentTypes->keys()->all());

        $qb->andWhere('content.id IN (:ids)')
            ->setParameter('ids', $ids);

        return $this->createPaginator($qb->getQuery(), $page, $amountPerPage);
    }

I don't seem to be too successfull with PRs but can try to pr these if you think they may help like?

@JTNMW
Copy link
Contributor

JTNMW commented Jun 30, 2020

as an aside, i just have a simple search form on the listing page /taxa which submits to itself eg /taxa?search=xxxxx

<form class="field has-addons" method="get" action="/taxa" enctype="text/plain">


						<label class="label is-hidden" for="search">Search</label>
						<div class="control">
							<input class="input" type="search" value="{% if app.request.get('search') is defined %}{{ app.request.get('search')|escape }}{% endif %}" placeholder="{{ __('general.phrase.search-ellipsis') }}" name="search" id="search" style="width: 9rem;">


						</div>
						<div class="control">
							<button class="button is-black" type="submit">{{ __('action.search') }}</button>
						</div>
					</form>`

@I-Valchev
Copy link
Member Author

@JTNMW you can also take a look at this:
https://bolt.tips/entry/7

pretty simple :-)

@JTNMW
Copy link
Contributor

JTNMW commented Jun 30, 2020

@I-Valchev, ok, that's great, thanks. That would seem to mitigate me having to use custom listingController.php and contentRepository.php. I think i changed these as i wanted the search (anyField) to be able to search in taxonomies as well as fields, but ive since added this ability to selectQuery.php (see pr #1541) which i gather is used by setcontent anyway.

would this method work with multiple (and unknown) query parameters on submission of a form with many options? what would be the best way to get all app.request params and loop through adding to the where {}. ??

for example, if i have the the url /taxa?genera=abana&continent=Nearctic after submitting a form on the listing page (where genera is a taxonony slug, and continent is a field) could the twig end up bing created dynamically like this?

{% setcontent records = contenttype.slug where { 'genera': 'abana', 'continent': 'nearctic' } %}

@I-Valchev
Copy link
Member Author

I-Valchev commented Jun 30, 2020

@JTNMW
that should work, yes. If you loop over it and add the object properties that way.

Here's some pointers:
https://stackoverflow.com/questions/7719838/updating-object-properties-in-twig

And then you'd pass that object after the where, like so:
{% setcontent records = contenttype.slug where object %}

@I-Valchev
Copy link
Member Author

See https://bolt.tips/entry/7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants