Trying do create a "like system" for a simple application with Laravel and Livewire. I have managed to add likes, but I only want the user to be able to add one (1) like to a post. At the moment a user can add as many likes as he or she wants.
This is my current function to store likes:
public function storeLike()
{
// Check if the user already has liked the post
if($this->collection->likes()->exists()){
return $this->collection->likes()->delete();
}
// If not, add one like to the db
$like = $this->collection->likes()->make();
$like->user()->associate(auth()->user());
// Save the like
$like->save();
}
And the part that im struggling with is:
if($this->collection->likes()->exists()){
return $this->collection->likes()->delete();
}
It deletes all the likes for that post. So how can a disassociate, detach that like if it exists?
This is how I have made the collection:
$collection = collect();
$posts = Post::search('topic', $this->search)->with(['user'])->latest()->get();
$urls = Urls::search('topic', $this->search)->with(['user'])->latest()->get();
$news = News::search('topic', $this->search)->latest()->get();
/** Push posts to the collection */
foreach ($posts as $post) $collection->push($post);
/** Push urls to the collection */
foreach ($urls as $item) $collection->push($item);
/** Push news to the collection */
foreach ($news as $item) $collection->push($item);
I think the toggle (see docs) method could be very handy.
Let's assume we're building a component for a model called Post.
Livewire
public function clickedLike() {
$this->post->likes()->toggle(Auth::user());
}
Template
<button wire:click="clickedLike">
<3
</button>
Model
class Post extends Model {
public function likes() {
return $this->hasMany(User::class, 'likes')
}
}
Related
I would like to recover the slug of 2 categories from my routes but can’t write the Controller.
My Route
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
My Controller
public function viewoccupationcity($slug)
{
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
Your controller will accept the parameters from your route as variables by order
public function viewoccupationcity($ocupation, $city)
{
...
}
Example:
URL: technicians/o/foo/c/bar
public function viewoccupationcity($ocupation, $city)
{
// $ocupation will be 'foo'
// $city will be 'bar
}
Ok, you would need to retrieve 2 variables as that is what you are passing
public function viewoccupationcity($occupation, $city)
If you want the whole slug to do another search then you would use the $request object. So like so
public function viewoccupationcity(Request $request, $occupation, $city){ // You also need to include the Request decleration
$slug = $request->path();
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
EDIT: We are having to do a lot of guesswork as your question isn't very clear. I think what you are trying to achieve is probably this
public function viewoccupationcity($occupation, $city){
$technicians = TechnicianResource::collection(occupation::where('city',$city)->where('occupation',$occupation)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
If you need something more then you need to give more details
Imagine these 2 entities:
Article
title
description
category <- ManyToOne
Category
name
Say we need to manage an article with a form and the category attached.
$builder
->add('name')
->add('description')
->add('category')
This one will allow me to select from existing categories.
Now I'd like to be able to create categories if needed.
Title [ ]
Description
[ ]
[ ]
Category [ ]
The category field would be a free text box.
If the text corresponds to no category, a new one would be created and attached.
I tried with some DataTransformer with no luck
I need a reusable solution to manage that because I'll need to embed it especially in another form as a collection.
How can I do it reusable ?
Suggest that you do an "if" on your category.
When you check your form submission is valid and submitted, get the 'category' data, and if not exists persist it as a new category then query for the article after.
The code might be something like this:
if ($form-isSubmitted() && $form->isValid()){
$catName = $form->get('category')->getData(); // Get category.
// Query if it exists.
$qb = $em->createQueryBuilder();
$qb->select('c')
->from('AppBundle:Category', 'c')
->where('c.name = :catName')
->setParameter('catName', $catName);
$cat_results = $qb->getQuery()->setMaxResults(1)->getOneOrNullResult();
if ($cat_results == null){
$newCat = new Category();
$newCat()->setname($catName);
$em->persist($newCat);
$em->flush();
}
else{
...
\\ Render your form etc...
}
Hopefully you get the idea. The above code is simpler, because then you create the category in the same form.
Here is what I ended up with using a DataTransformer
My ArticleType:
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('description')
->add('category', CategoryType::class)
//...
A new CategoryType class:
class CategoryType extends AbstractType
{
protected $myService;
public function __construct(MyService $myService)
{
$this->myService = $myService;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new CategoryTransformer($this->myService);
$builder->addModelTransformer($transformer);
}
}
Registered as service
app.form.category_type:
class: AppBundle\Form\CategoryType
arguments: ['#app.my.service']
tags:
- { name: form.type }
And finally the transformer:
class CategoryTransformer implements DataTransformerInterface
{
// ...MyService initialization...
public function transform($category)
{
if (null === $category) {
return '';
}
return $category->getName();
}
public function reverseTransform($categoryName)
{
if (!$categoryName) {
return null;
}
$category = $this->myService->getOrCreateCategoryFromName($categoryName);
if (null === $category) {
throw new TransformationFailedException();
}
return $category;
}
}
MyService is responsible to get or create a category with the given name using the entity manager.
Quite some lines of code but once this is done, wherever I'll use my form, it can be dealt in the easy standard way:
$articleForm = $this->createForm(ArticleType::class, $article);
$articleForm->handleRequest($request);
if ($articleForm->isValid()) {
$em->persist($article);
$em->flush();
//...
I am currently making an edit page for some data in my database, and on the edit page I am trying to make a Form::select which lists the people in my users table.
controller-which-makes-the-edit-view.php
<?php
class AdminController extends BaseController
{
public $restful = true;
public function getUpdatePage($id)
{
return View::make('data_edit')
->with('title', 'Rediger måling/oppgave')
->with('data', Routine::find($id))
->with('emp', Emp::lists('user_name', 'id'));
}
data_edit.blade.php
{{ Form::label('emp', 'Ansatt') }}
{{ Form::select('emp', $emp, $data->emps->user_name) }}
Now my question is how would I go about making the default value for the select the person that saved the row which is currently being edited?
I do apologize if this already is answered, I couldn't seem to find it (neither here nor google).
This is Form::select() definition:
public function select($name, $list = array(), $selected = null, $options = array())
{
}
The third parameter is the item to be selected. You are currently passing
$data->emps->user_name
To it, but it depends on the data you have on $emp, because you must pass to it the array key, not the value.
Note that for now (Laravel 5.3+), ::lists is obsolete, use ::pluck instead.
I have controller and i am getting data like this
$events= Eventm::with('teacher')->get();
Every event has one teacher then in view i am taking data like
$event['teacher']->name
its work fine if their their is relation between teacher and events. but if their is no teacher in teacher column (or Admin delete the teacher but still event contain the teacher id). the view don't render at all and show error trying to get property of non object. I want that if teacher data is deleted the view will not throw error , just show no text on
$event['teacher']->name
is it possible ?
You can do in your view
#if($event['teacher'])
<div class="whatever">$event['teacher']->name</div>
#else
<div class="whatever">Teacher not found.</div>
#endif
Another possibility is to do what you should be doing anyway: pass from controller the data to be displayed in the view. Your view should not be complicated and it should not be aware of your objects and your data, you should pass to it the data it is supposed to display. This is somethig you should be doing in a presenter or just in another class:
<?php
class ProcessEventsViewData {
private $events;
public function __construct($events)
{
$this->processData($events);
}
public function processData($data)
{
foreach($data as $event)
{
$this->processEvent($event);
}
}
public function processEvent($event)
{
$this->events[$event->id]['name'] = $event->name ?: 'Event name not found';
$this->events[$event->id]['teacher'] = $event->teacher ? $event->teacher->toArray() : array('name', 'Teacher not found');
}
public function getData()
{
return $this->events;
}
}
class MyController extends Controller {
$events = Eventm::with('teacher')->get();
$eventData = new ProcessEventsViewData($events);
return View::make('event')->with('events', $eventData->getData());
}
And in your view you now can just:
#foreach($events as $id => $event)
Event ID: {{$id}};
Event Name: {{$event['name']}};
Teacher: {{$event['teacher']['name']}};
#endforeach
I would like a best practice for this kind of problem
I have items, categories and category_item table for a many to many relationship
I have 2 models with these validations rules
class Category extends Basemodel {
public static $rules = array(
'name' => 'required|min:2|max:255'
);
....
class Item extends BaseModel {
public static $rules = array(
'title' => 'required|min:5|max:255',
'content' => 'required'
);
....
class Basemodel extends Eloquent{
public static function validate($data){
return Validator::make($data, static::$rules);
}
}
I don't know how to validate these 2 sets of rules from only one form with category, title and content fields.
For the moment I just have a validation for the item but I don't know what's the best to do:
create a new set of rules in my controller -> but it seems redundant
sequentially validate Item then category -> but I don't know how to handle validations errors, do I have to merges them? and how?
a 3rd solution I'm unaware of
here is my ItemsController#store method
/**
* Store a newly created item in storage.
*
* #return Redirect
*/
public function store()
{
$validation= Item::validate(Input::all());
if($validation->passes()){
$new_recipe = new Item();
$new_recipe->title = Input::get('title');
$new_recipe->content = Input::get('content');
$new_recipe->creator_id = Auth::user()->id;
$new_recipe->save();
return Redirect::route('home')
->with('message','your item has been added');
}
else{
return Redirect::route('items.create')->withErrors($validation)->withInput();
}
}
I am very interested on some clue about this subject
thanks
One way, as you pointed yourself, is to validate it sequentially:
/**
* Store a newly created item in storage.
*
* #return Redirect
*/
public function store()
{
$itemValidation = Item::validate(Input::all());
$categoryValidation = Category::validate(Input::all());
if($itemValidation->passes() and $categoryValidation->passes()){
$new_recipe = new Item();
$new_recipe->title = Input::get('title');
$new_recipe->content = Input::get('content');
$new_recipe->creator_id = Auth::user()->id;
$new_recipe->save();
return Redirect::route('home')
->with('message','your item has been added');
}
else{
return Redirect::route('items.create')
->with('errors', array_merge_recursive(
$itemValidation->messages()->toArray(),
$categoryValidation->messages()->toArray()
)
)
->withInput();
}
}
The other way would be to create something like an Item Repository (domain) to orchestrate your items and categories (models) and use a Validation Service (that you'll need to create too) to validate your forms.
Chris Fidao book, Implementing Laravel, explains that wonderfully.
You can also use this:
$validationMessages =
array_merge_recursive(
$itemValidation->messages()->toArray(),
$categoryValidation->messages()->toArray());
return Redirect::back()->withErrors($validationMessages)->withInput();
and call it in the same way.
$validateUser = Validator::make(Input::all(), User::$rules);
$validateRole = Validator::make(Input::all(), Role::$rules);
if ($validateUser->fails() OR $validateRole->fails()) :
$validationMessages = array_merge_recursive($validateUser->messages()->toArray(), $validateRole->messages()->toArray());
return Redirect::back()->withErrors($validationMessages)->withInput();
endif;