I'm working on a small Symfony project and I came across an issue I don't know how to fix. I'm performing a database search via a GET request, and I'm looking for articles that are part of categories. I can select many categories and press "search", then it will return only the specific articles.
The feature is working great, but my URL isn't... "clean", I think. Working on localhost, this is what I see, for example, when I look for category "vocabulary":
localhost:8000/blog/?q=&categories[]=125
The q parameter is here to return articles when I look for a specific text (with an input field), but I think it shouldn't appear if the research only concerns categories. Right?
Also, display categories[]=125 is weird. The user should see something like categories=vocabulary&categories=grammar&categories... or something like this (of course, trying to avoid the repetition of "categories" in the URL.
How can I do this? This is what my code looks like in the repository:
/**
* Find articles related to search
*/
public function findSearch(SearchData $search): PaginationInterface
{
$query = $this
->createQueryBuilder('a')
->select('a', 'c')
->join('a.categories', 'c');
// Handling text search
if (null !== $search->q && !empty($search->q)) {
$query = $query
->andWhere('a.title LIKE :q')
->setParameter('q', "%{$search->q}%");
}
// Handling categories
if (!empty($search->categories)) {
$query = $query
->andWhere('c.id IN (:categories)')
->setParameter('categories', $search->categories);
}
$query = $query->getQuery();
return $this->paginator->paginate(
$query,
$search->page,
9
);
}
And here is what my controller looks like:
#[Route('/', name: 'blog_index')]
public function index(ArticleRepository $articleRepository, Request $request): Response
{
$data = new SearchData();
$data->page = $request->get('page', 1);
$form = $this->createForm(SearchType::class, $data);
$form->handleRequest($request);
$articles = $articleRepository->findSearch($data);
return $this->render('blog/index.html.twig', [
'form' => $form,
'articles' => $articles,
]);
}
If you need more info in order to help me, please ask.
Thanks in advance!
Related
I need a little help and I can’t find an answer. I would like to replicate a row from one data table to another. My code is:
public function getClone($id) {
$item = Post::find($id);
$clone = $item->replicate();
unset($clone['name'],$clone['price']);
$data = json_decode($clone, true);
Order::create($data);
$orders = Order::orderBy('price', 'asc')->paginate(5);
return redirect ('/orders')->with('success', 'Success');
}
and i got an error :
"Missing argument 1 for
App\Http\Controllers\OrdersController::getClone()"
.
I have two models: Post and Order. After trying to walk around and write something like this:
public function getClone(Post $id) {
...
}
I got another error
Method replicate does not exist.
Where‘s my mistake? What wrong have i done? Maybe i should use another function? Do i need any additional file or code snippet used for json_decode ?
First of all, make sure your controller gets the $id parameter - you can read more about how routing works in Laravel here: https://laravel.com/docs/5.4/routing
Route::get('getClone/{id}','YourController#getClone');
Then, call the URL that contains the ID, e.g.:
localhost:8000/getClone/5
If you want to create an Order object based on a Post object, the following code will do the trick:
public function getClone($id) {
// find post with given ID
$post = Post::findOrFail($id);
// get all Post attributes
$data = $post->attributesToArray();
// remove name and price attributes
$data = array_except($data, ['name', 'price']);
// create new Order based on Post's data
$order = Order::create($data);
return redirect ('/orders')->with('success', 'Success');
}
By writing
public function getClone(Post $id)
you are telling the script that this function needs a variable $id from class Post, so you can rewrite this code like this :
public function getClone(){
$id = new Post;
}
However, in your case this does not make any sence, because you need and integer, from which you can find the required model.
To make things correct, you should look at your routes, because the url that executes this function is not correct, for example, if you have defined a route like this :
Route::get('getClone/{id}','YourController#getClone');
then the Url you are looking for is something like this :
localhost:8000/getClone/5
So that "5" is the actual ID of the post, and if its correct, then Post::find($id) will return the post and you will be able to replicate it, if not, it will return null and you will not be able to do so.
$item = Post::find($id);
if(!$item){
abort(404)
}
Using this will make a 404 page not found error, meaning that the ID is incorrect.
I am using Laravel 4 for a new project, which is a news site.
The URL for a single article should be like this:
domain.com/{category}/{shorturl}/{id}
(for example "domain.com/sports/realmadrid-barcelona/15")
In my routes.php file I have declared:
Route::get('{cat}/{shorturl}/{id}', 'ArticlesController#view');
which is working fine.
Now all I need to do is use a single Article (model) instance to pass as parameter in the Controller action to generate the URL/Route. This means, instead of passing all three parameters ('cat', 'shorturl' and 'id'), I would like to pass the Article instance.
To manage this, what I did so far is the following:
In routes.php:
Route::model('article', 'Article');
Route::get('{cat}/{shorturl}/{id}', 'ArticlesController#view');
Route::get('article/{article}', 'ArticlesController#generateUrl');
In ArticlesController.php:
public function view($cat, $urltext, $id)
{
$article = Article::findOrFail($id);
return View::make('articleView', compact(array('article')));
}
public function generateUrl(Article $article)
{
$cat = $article->category()->pluck('text');
$shorturl = $article->urltext;
$id = $article->id;
return Redirect::action('ArticlesController#view', array('cat' => $cat, 'shorturl' => $shorturl, 'id' => $id));
}
By doing this, in my view file I have something like this to produce links to other articles:
{{ $otherarticle->title }}
The problem is that, although the redirect works, the actual URL (the one shown on mouse hover) is the 'original' one (this means: "domain.com/article/123") instead of the intended ("domain.com/sports/realmadrid-barcelona/123").
Any ideas on how to accomplish this? I would like to only use $article (the Article model instance) to generate URLs, in order to keep the code as simple and clean as possible.
Thank you,
Ilias
Instead of using a redirect you need to generate the real url right away.
I would add this method to your model class
class Article extends Eloquent {
public function getUrl(){
$cat = $this->category()->pluck('text');
$shorturl = $this->urltext;
$id = $this->id;
return URL::action('ArticlesController#view', array('cat' => $cat, 'shorturl' => $shorturl, 'id' => $id));
}
}
And then just call it on the article object $article->getUrl() to generate the url
Here's my route :
Route::get('location/{id}/{title?}', array('as' => 'scouting_single', 'uses' => 'ScoutingController#get_single'));
The class is simple:
public function get_single($id, $title ='') {
$location = new Location;
if(is_numeric($id)) {
$location = $location->find($id);
if(isset($location)) {
$author = User::find($location->author);
$meta = $location->find($id)->metakeywords;
if($title == '') {
$slug = Str::slug($location->title);
return Redirect::to('location/'.$id.'/'.$slug);
}
return View::make('scoutingviews.view')->with('pagetitle', 'Location view')
->with('location', Location::find($id))
->with('author', $author)
->with('meta', $meta);
} else {
return Redirect::to('/')->with('message', 'That record is not available');
}
} else {
return Redirect::to('404');
}
}
Everything seems to work fine but after searching around it seems that others are doing it differently like saving the slugs to db, but I just want to include the id to the url... and make it optional to include the title slug. If the user removes the slug, it will redirect the user with the slug anyways.
I'm still learning laravel so please forgive the newbie question with regards to seo-friendliness, I just don't want /{id}/ and /{id}/{title} to count as duplicates
If you are worried about SEO, then having a URL slug in the database is the way to go. Having the ID in the URL isn't very helpful to search engines or users. This should also simplify your code as well. In addition to that, you should take a look at Eloquent relationships where you can attach your users to locations. Instead of having
$author = User::find($location->author);
You could have
$author = $location->author;
The relationship would be a User hasMany Locations, and Location belongsTo User. This is also assuming that users can have many locations.
I have a Model for Groups and another model for Notes (Notes and Posts are same things).
NotesController:
public function groupnotes()
{
if (!empty($this->data))
{
$data = $this->data;
$data['Note']['user_id'] = $this->Auth->user('id');
if ($this->Note->save($data))
{
PROBLEM HERE
}
}
if(empty($this->data['Note']['notes']))
{
PROBLEM HERE
}
GroupsController: (ViewCourse is used to view each group )
public function viewcourse($id=NULL)
{
$this->set('viewcourse', $this->Group->read(NULL,$id));
$this->set('course', $this->Group->find('all', array('conditions'=>array('Group.id'=>$id))));
}
Now when i create a post in a group it redirects me to "groupnotes" action and i want it to redirect me to viewcourse/id ... I am a bit confused how can i redirect the page to viewcourse/id ...
I tried doing it by adding this to groupnotes action
$this->redirect(array('controller'=>'groups', 'action' => 'viewcourse'));
but here i do not have the id.
What do you suggest?
This question might help you: What is the equivalent to getLastInsertId() in Cakephp?
$this->redirect(array(
'controller'=>'groups',
'action' => 'viewcourse/'.$this->Group->getLastInsertId())
);
EDIT:
I have only suggested that you go to the last inserted id of a group as a suggestion. Your question is a bit vague when you say "but here i do not have the id."
1. are you looking to go to any valid course id?
2. last entered course?
3. first entered course id?
Alternatively you could set a default course in your controller like so...
public function viewcourse($id=NULL)
{
if(!$id){
$id = $this->Group->find('first');
$id = $id['Group']['id'];
}
$this->set('viewcourse', $this->Group->read(NULL,$id));
$this->set('course', $this->Group->find('all', array('conditions'=>array('Group.id'=>$id))));
}
NOTE: Just a tip,
$this->set('course', $this->Group->find('all', array('conditions'=>array('Group.id'=>$id))));
Can be substituted with
$this->set('course', $this->Group->findById($id));
To make your code a bit leaner
new to CodeIgniter and MVC/OOP as well. My current problem that I am trying to work through involves 2 tables.
Gallery Table
id
name
clientID
Client Table
id
Name
The gallery['clientID'] references the client['id'] so I can retrieve the name. Currently my gallery_model.php file looks like
class Gallery_model extends CI_Model
{
public function __construct()
{
$this->load->database();
}
//Get all in progress galleries from client
public function get_progress($id = FALSE , $clientRef = '205')
{
if($id == FALSE) {
$query = $this->db->get_where('gallery', array('clientRef' => $clientRef, 'finish' => '0' ));
return $query->result_array();
}
}
//Get all proofed galleries from client
public function get_proofed($id = FALSE , $clientRef = '205')
{
//get all galleries from client
if ($id == FALSE) {
$query = $this->db->get_where('gallery',array('clientRef' => $clientRef, 'finish' => '1'));
return $query->result_array();
}
}
//get the gallery selected
public function get_gallery($id , $clientRef = '205')
{
//This returns individual galleries
$query = $this->db->get_where('gallery', array('id' => $id));
return $query->row_array();
}
}
My controller looks like:
public function index()
{
//Proofed Albums
$data['gallery'] = $this->gallery_model->get_proofed();
//Albums that are in progress
$data['in_progress'] = $this->gallery_model->get_progress();
$this->load->view('templates/header',$data);
$this->load->view('gallery/index',$data);
$this->load->view('templates/footer');
}
Then the view's out put is
$gallery['name'] - $gallery['clientId']
What is the best practice for something like this. I know it's probably simple, but I want to start out doing this correctly. Should I use $this->db->join();
Thanks in advance for the help on this.
Following up on William's answer, you can do a join using CI's Active Record
$this->db->from('gallery')->join('client', 'gallery.id = client.id')->get()
Using $this->db->join() is indeed the best (and the only way done via Active Records without adding your own SQL) way to get information from several tables all in one query.
You're probably already aware of this, but just in case (and for the benefit of future people visiting this page), the CodeIgniter User Guide has a nice page detailing how to use Active Records.
The default of Inner Join should be fine for your purposes. If you have gallery entries without clients linked to them and you want them to be included in the results, then you may want to consider the other types of join, which you can read about here.