Laravel, generate a new link automatically - laravel

I have created a laravel project, and it includes 4 different blog entries. I want each blog entry to create its own url using the blogs title, so for example /blog/{blog:id}, however I am unsure to do this.
Route::get('/', function () {
$links = \App\Link::paginate(2);
return view('welcome', ['links' => $links]);
});
Route::get('/{links:id}', function (Links $links) {
return $links;
});

One option would be to create links from your Blog title.
//Blog model
function getUrl(){
return $this->title; //Blog title
}
This way you can generate links directly using Blog model instead of using the Link class.
Then when showing your Blog, you will have to do something like this to retrieve by blog title rather than blog id:
$blog = Blog:where('title', $request->title)->first();
This option might need tinkering though, since you might have Blogs with the same title. If you have the same title, you can also use the Blog id in the getUrl() in order to be able to find the correct Blog object.
Another option I would prefer better would be to use one of these packages, which helps you out with slugs:
https://github.com/spatie/laravel-sluggable
https://github.com/cviebrock/eloquent-sluggable

Related

Vuex and Laravel 5: how to remove Tags from Articles

I have a small blog app that has Articles and Tags. Nothing fancy so far. Every Article can have many Tags.
The Laravel backend delivers the data via API calls from Axios in the Vue Frontend. In the Laravel models Article has a method
public function tags(){
return $this->belongsToMany('App\Tag');
}
and vice versa for tags. I have a pivot table and all this follow pretty much the example given in https://laracasts.com/series/laravel-5-fundamentals/episodes/21
All this works fine.
Now let´s say I want to call in Vue the method deleteTag() which should remove the connection between Article and Tag. Things are behind the scenes a bit more complicated as "addTag" in PHP also adds a new Tag Model AND the connection between Tag and Article in the Pivot table OR connects - if the Tag exists already - an existing Tag with Article.
What is the best way to achieve this?
What I´m doing so far:
ArticleTags.vue
deleteTag(tagName){
let articleId = this.articleId;
this.$store.dispatch('removeTagFromArticle', { articleId, tagName });
},
index.js (Vuex store)
actions: {
removeTagFromArticle(context,param) {
axios.post('api/articles/delete-tag/', param)
.then((res) => {
let article = context.getters.getArticleById(param.articleId);
let tagName = param.tagName;
context.commit('deleteTag', {article, tagName} );
})
.catch((err) => console.error(err));
} }
mutations : { deleteTag (state, { article, tag }) {
let tagIndex = article.tags.indexOf(tag);
article.tags.splice(tagIndex,1);
} }
ArticleController.php
/**
* Remove Tag from Article
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function deleteTag(Request $request)
{
$tag = \App\Tag::firstOrCreate(array('name' => $request->tagName));
$article = Article::findOrFail($request->articleId);
$result = $article->tags()->detach([$tag->id]);
$this->cleanUpTags();
return response('true', 200);
}
routes/web.php
Route::post('api/articles/delete-tag', 'ArticleController#deleteTag');
This works so far. The code does exactly what it should. Only it feels really clumsy. And probably to complicated. Maybe it´s because the example is simple but the whole setup is big.
Nonetheless I´m trying to improve my coding. :)
So my questions are:
1) Would it be better to pass the article object in Vue to the store instead of the articleId?
2) Is the idea of using Array.slice() in the store too complicated? This could be done straight in the components.
3) Does it make sense to reload the whole store from Laravel after deleting the tag PHP-wise?
Edit: in case someone is looking for this question and how I solved it at the end. The source code for this app can be found at https://github.com/shopfreelancer/streamyourconsciousness-laravel
Personally I like to use ID's to reference any database resource aswell as keeping my objects in javascript somewhat the same as my API.
1
In this case I would have changed my tags to objects instead of strings and send an ID of the tag to my API.
An example of my article would look like:
let article = {
tags: [{ id: 1, name: 'tag 1' }, { id: 2 ... }]
}
Using objects or IDs as parameters are in my opinion both fine. I should stick with objects if you like "cleaner" code, there will only be one place to check if the ID is present in your object aswell as the selection of the ID.
Case:
// Somehwere in code
this.deleteArticle(article);
// Somehwere else in code
this.deleteArticle(article);
// Somewhere else in code
this.deleteArticle(article);
// Function
deleteArticle(article) {
// This check or other code will only be here instead of 3 times in code
if (!article.hasOwnProperty('id')) {
console.error('No id!');
}
let id = article.id;
...
}
2
Normally I would keep the logic of changing variables in the components where they are first initialized. So where you first tell this.article = someApiData;. Have a function in there that handles the final removal of the deleted tag.
3
If you are looking for ultimate world domination performance I would remove the tag in javascript. You could also just send the updated list of tags back in the response and update all tags of the article with this data and keep your code slim. However I still like to slice() the deleted tag from the array.
Remember this is my opinion. Your choises are completely fine and you should, like I do myself, never stop questioning yours and others code.

How to render a cms page with default theme AND variables from controllers in OctoberCMS?

I'm wondering how I can render a view, or display a page with my default theme in OctoberCMS, via a route that executes a function in a controller.
If I have the following route:
Route::get('bransje', [
'uses' => 'Ekstremedia\Cityportal\CPController#bransje'
]);
And in my controller CPController ive tried several things, like I used to with Laravel:
public function bransje() {
$stuff = Stuff::with('info');
return View::make('cms::bransje')->with('stuff',$stuff);
}
But I cannot seem to get it to work, and I've tried to search the web, but it's hard to find answers. I have found a workaround, and that is to make a plugin component, then I can include that component and do:
public function onRun()
{
$this->eventen = $this->page['stuff'] = $this->stuff();
}
protected function stuff()
{
return ...
}
Is there any way so I can make pages without using the Cms, and that are wrapped in my default theme? I've tried
return View::make('my-theme-name::page');
and a lot of variants but no luck.
I know I can also do a:
==
public function onRun()
{
}
in the start of my page in the cms, but I'm not sure how to call a function from my plugin controller via there.
You can bypass frontend routing by using routes.php file in your plugin.
Full example in this video turotial.
If this answer can still be useful (Worked for October v434).
I have almost the same scenerio.
What I want to achieve is a type of routing like facebook page and profile.
facebook.com/myprofile is the same url structure as facebook.com/mypage
First I create a page in the CMS for each scenario (say catchpage.htm)
Then a created a catchall route at the buttom of routes.php in my plugin that will also not disturb the internal working of octobercms.
if (!Request::is('combine/*') && !Request::is('backend/*') && !Request::is('backend')) {
// Last fail over for looking up slug from the database
Route::get('{slug}/{slug2?}', function ($slug, $slug2 = null) {
//Pretend this are our routes and we can check them against the database
$routes = ["bola", "sade", "bisi", "ade", "tayo"];
if(in_array($slug, $routes)) {
$cmsController = new Cms\Classes\Controller;
return $cmsController->render("/catchpage", ['slug' => $slug]);
}
// Some fallback to 404
return Response::make(View::make('cms::404'), 404);
});
}
The if Request::is check is a list of all the resource that october uses under the hood, please dont remove the combine as it is the combiner route. Remove it and the style and script will not render. Also the backend is the url to the backend, make sure to supply the backend and the backend/*.
Finally don't forget to return Response::make(View::make('cms::404'), 404); if the resource is useless.
You may put all these in a controller though.
If anyone has a better workaround, please let us know.

Associate relationship afterwards

Version used: Laravel 5.4
We have a post and that post can have several pictures associated with it. We use a controller to store the post and an other one to store the pictures.
We set the relationship in the models like so:
class Post extends Model
{
public function pictures()
{
return $this->hasMany(Picture::class);
}
}
class Picture extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
}
When I go to the form to create a new post, I can add pictures to that post before I actually store the post. Which means that when I store a picture I still don't have a post id to associated it to.
My questions is:
Using only php, is there a clean way to associate the pictures to the post before or afterwards ?
The solution I am currently using is that when I go to the form to create a new post, a blank new post is created before hand and I pass the id on the url. It has to be a better way to do it.
You can try this
First create the form when user access the create post page (as you are already doing).
Suppose this is your function which is showing the create post view
public function showCreatePost()
{
$post = Post::create(['field_name' => '', // put your attributes]);
// pass other data as well if you want.
return view('your_create_view',compact('postId',$postId));
}
change your route of imageupload which will have postid . for example
Route::post('post/image/{postId}','SomeController#somefunction');
create one route for editing the post and use it for editing the post which will have post id.
Route::post('post/edit/{postId}','SomeController#someOtherfunction');
Now use these two routes as form action and you can edit the post and upload the image. route example below
Showing the id in url is not a good idea. so by this way you can try and let me know if you face any problem.

show name instead of id in a url using laravel routing

I have defined a route in laravel 4 that looks like so :
Route::get('/books/{id}', 'HomeController#showBook');
in the url It shows /books/1 for example , now i'm asking is there a way to show the name of the book instead but to keep also the id as a parameter in the route for SEO purposes
thanks in advance
You could also do something like this:
Route::get('books/{name}', function($name){
$url = explode("-", $name);
$id = $url[0];
return "Book #$id";
});
So you can get book by id if you pass an url like: http://website.url/books/1-book-name
if your using laravel 8, this may be helpfull.
In your Controller add this
public function show(Blog $blog)
{
return view('dashboard.Blog.show',compact('blog'));
}
In your web.php add this
Route::get('blog/{blog}', [\App\Http\Controllers\BlogController::class,'show'])->name('show');
Then add this to your model (am using Blog as my Model)
public function getRouteKeyName()
{
return 'title'; // db column name you would like to appear in the url.
}
Note: Please let your column name be unique(good practice).
Result: http://127.0.0.1:8000/blog/HelloWorld .....url for a single blog
So no more http://127.0.0.1:8000/blog/1
You are welcome.
You can add as many parameters to the url as you like, like this:
Route::get('/books/{id}/{name}', 'HomeController#showBook');
Now when you want to create an url to this page you can do the following:
URL::action('HomeController#showBook', ['id' => 1, 'name' => 'My awesome book']);
Update:
If you are certain that there will never be two books with the same title, you can just use the name of the book in the url. You just need to do this:
Route::get('/books/{name}', 'HomeControllers#showBook');
In your showBook function you need to get the book from the database using the name instead of the id. I do strongly encourage to use both the id and the name though because otherwise you can get in trouble because I don't think the book name will always be unique.
You can also use model binding check more on laravel docs
For example
Route::get('book/{book:name}',[BookController::class,'getBook'])->name('book');
The name attribute in "book/{book:name}" should be unique.

CodeIgniter hide post id and only slug URL

I'm working on custom blog based on CodeIgniter. Got some problems at the moment I've achieved url:
/blog/view/1/my-very-first-post
I'm not happy with that I'd like to get rid off id and "/view"
That's how my controller looks like:
function index($postId=null)
{
$this->view($postId=null);
}
function view($postId, $str_slug = '')
{
$data['title'] = ucfirst("Blog");
$data['post'] = $this->posts->get_posts($postId);
if($postId !== null)
{
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/single_view', $data);
$this->load->view('templates/footer',$data);
} else {
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/index', $data);
$this->load->view('templates/footer',$data);
}
$row = $this->db->get_where('posts', array('id' => $postId))->row();
if ($row and ! $str_slug) {
$str_slug = url_title($row->title, 'dash', TRUE);
redirect("blog/view/{$postId}/{$str_slug}");
}
}
What is the best way to achieve this?
Thanks!!
Adam
I think you're looking for the _remap() method. You can read more about it here: http://ellislab.com/codeigniter/user-guide/general/controllers.html#remapping
Your code would look something like below. You still need to implement the get_posts_by_slug() method if you still need it.
public function _remap($slug) {
$data['title'] = ucfirst("Blog");
$data['post'] = $this->posts->get_posts_by_slug($slug);
if($slug !== null)
{
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/single_view', $data);
$this->load->view('templates/footer',$data);
} else {
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/index', $data);
$this->load->view('templates/footer',$data);
}
$row = $this->db->get_where('posts', array('slug' => $slug))->row();
}
Update:
Hey #AdamLesniak - it's generally good design to store a permalink to a blog post or a news article, so that it's not dependent on the title or any other volatile data structure in order to still be accessible.
But for a different approach, personally, I really think this is a nice system:
http://localhost/blog/my-news-article-title-permalink/3
The main issue is that if you don't store the slug/permalink somewhere, then you're going to feel sad when somebody changes the title of an article. Example, my article called "Hello World First Blog Post", which is accessible at:
http://localhost/blog/hello-world-first-blog-post
Changes into "First post I ever made":
http://localhost/blog/first-post-i-ever-made
So what happens to the initial URL? It's no longer accessible - any user that comes on the website via a search engine, or through somebody's link will now no longer see your comment and will instead get a 404 page, which you want to avoid.
A problem with using permalinks on their own is that you need to make sure they're unique, and extra constraints need to be set in place for that.
There are a few tricks that you can do, but they all have their pitfalls. You can for instance use the system I've mentioned above, where you stick the unique identifier at the end of the URL:
http://localhost/blog/hello-world/3
And if the title changes, you don't really care, because you're not using the slug to make your searches, but instead, you're relying on the unique identifier.
http://localhost/blog/first-post-i-ever-made/3
However I've seen opinions that this sort of system is against the idea of an URL (Uniform Resource Locator). I've used it in back when I was starting out, and it proved to be a flexible system; it's definitely nice to experiment with at least.
BBC do a variant of the above, by keeping the category that an article belongs to and the unique identifier for the entry:
http://www.bbc.co.uk/news/business-24511283
Basically, they know that an article will never change its category, although it may change its title, so they just keep the general topic, which is business and the unique identifier 24511283.
In the end, what I suggest you do, as it's by far the most scalable solution is to just generate the following format:
http://localhost/blog/permalink/unique-id
The format above lets you have unique identifiers, which are important for guaranteeing singularity, and permalinks for all the search engine friendly-ness! Now if the title of the article changes, display the updated title to the user on the page, but don't do anything to the permalink, so that your URLs never change.
By also using IDs in the URL, you know for sure that you can use a permalink multiple times, and your system will still scale correctly.
I wrote a tutorial on this which may or may not help: http://www.rappasoft.com/tutorials/14-seo-friendly-links-with-codeigniter.html

Resources