Submitting like using Ajax within Laravel application - php

So i've modified some code to add a Like function to a table called sources, this sources have the relation "hasMany" with the table likes.
It doesn't work and I don't get any errors, neither Javascript or PHP errors.
When I like the page it seems like the Ajax don't get called because the page reload to /public/view#, so nothing get submitted to the database.
I don't know how to sort this out.
View:
#foreach($post->sources as $source)
<a>{{$source['link']}}</a>
<div class="interaction" data-sourceid=" {{ $source['id'] }} ">
{{ Auth::user()->likes()->where('source_id', $source['id'])->first() ? Auth::user()->likes()->where('source_id', $source['id'])->first()->like == 1 ? 'You like this source' : 'Like' : 'Like' }} |
{{ Auth::user()->likes()->where('source_id', $source['id'])->first() ? Auth::user()->likes()->where('source_id', $source['id'])->first()->like == -1 ? 'You don\'t like this source' : 'Dislike' : 'Dislike' }}
</div>
<br>
#endforeach
<script>
var token = '{{ csrf_token() }}';
var urlLikeSource = '{{ route('likesource') }}';
</script>
app.js:
$('.like-source').on('click', function(event){
event.preventDefault();
sourceId = event.target.parentNode.dataset['sourceid'];
var isLike = event.target.previousElementSibling == null; //Checks if it's a like or dislike.
$.ajax({
method: 'POST',
url: urlLikeSource,
data: {isLike: isLike, souceId: sourceId, _token: token}
})
.done(function(){
//Change the page when .ajax has been executed.
event.target.innerText = isLike ? event.target.innerText == 'Like' ? 'You like this source' : 'Like' : event.target.innerText == 'Dislike' ? 'You don\'t like this source' : 'Dislike';
//Make sure you can't dislike and like at the same time.
if(isLike){
event.target.nextElementSibling.innerText = 'Dislike';
} else {
event.target.previousElementSibling.innerText = 'Like';
}
});
});
routes:
Route::post('/likesource', [
'uses' => 'SourceController#sourceLikeSource',
'as' => 'likesource'
]);
SourceController.php:
<?php
namespace App\Http\Controllers;
use App\Comment;
use App\Post;
use App\Like;
use App\Source;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
class SourceController extends Controller{
public function sourceLikeSource(Request $request)
{
$source_id = $request['sourceId'];
$is_like = $request['isLike'] === 'true'; //Get's passed as string in request, changed to boolean.
$update = false;
//REDO WITH SMARTER SOLUTION
if($is_like == 0){
$is_like = -1;
}
$source = Source::find($source_id);
if(!$source){
return null;
}
$user = Auth::user();
$like = $user->likes()->where('source_id', $source_id)->first(); //First has to be specified
if($like){
$already_like = $like->like;
$update = true;
//Deletes if it already exists.
if($already_like == $is_like){
$like->delete();
return null;
}
} else {
$like = new Like(); //Creates new row for Like in table
}
$like->like = $is_like; //Set's whatever $like->like to whatever $request['isLike'] passed.
$like->user_id = $user->id;
$like->source_id = $source_id;
if($update){
$like->update();
}else{
$like->save();
}
return null;
}
}
Like.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Like extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function post()
{
return $this->belongsTo('App\Post');
}
public function source()
{
return $this->belongsTo('App\Source');
}
public function comment()
{
return $this->belongsTo('App\Comment');
}
}
Source.php model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Source extends Model
{
public function post()
{
return $this->belongsTo('App\Post');
}
public function likes()
{
return $this->hasMany('App\Like');
}
}
Edit:
Rookie mistake by me, I cleared the browser cache. The browser used an old app.js file. Now the ajax seemed to get called correctly, however there's still no new rows in the likes table.
Edit 2:
I gave the data within the Ajax call sourceId: 1 and isLike: 2. There were still no new rows being created in the table likes.
So the problem seems to be in the routes, controller or any of the models.
Edit 3:
I changed the route to a route I knew works properly, I added a csfr_token exception to make sure the token wasn't screwing anything up. I made sure everything in the models were right and I finally made sure everything in the controller was being called correctly.
I truly can't find any problem.
The only thing I can think about is that the likes table "belongsTo" a lot of tables. I made the post_id, comment_id and source_id nullable when I created the table migration.
Should I use some other relationship in the models?

Related

Laravel Update a div class from Controller

Is it possible to use a controller to update a div class? I'm still learning Laravel here and could use some help. I have a controller with the following code:
public function update()
{
if (Auth::check() || Auth::attempt()) {
$auth_id = Auth::user()->rcid;
} else {
$auth_id = '00000';
}
$finalize = DB::table('AdminOperations.gen_ed_assessment.responses_data_record')
->where('fkey_instructor_id' , $auth_id)
->update(['locked' => 1]);
return Redirect::to('/');
}
When I redirect back to the default or homepage I'd like to be able to update a a menu button from list-group-item to list-group-item disabled.
My button code below:
Enter Your Assessments<span class="badge"><?php echo $totalrecordstofillout;?></span>
How would I parse the code in the controller to make this disabled when its redirected.
Can you use the controller to change the div class and if so how would you go about that in the controller?
Sorry if this is an elementary question for those php/laravel pros.
Thanks in advance.
You can use session flash, example:
public function update()
{
if (Auth::check() || Auth::attempt()) {
$auth_id = Auth::user()->rcid;
} else {
$auth_id = '00000';
}
$finalize = DB::table('AdminOperations.gen_ed_assessment.responses_data_record')
->where('fkey_instructor_id' , $auth_id)
->update(['locked' => 1]);
if ($someCondition === true) {
Session::flash('is_disabled', true);
}
return Redirect::to('/');
}
on your view:
Enter Your Assessments<span class="badge"><?php echo $totalrecordstofillout;?></span>
Edit:
I noticed that you are using Laravel 4, so I've edited my answer.

Post Data not working correctly Laravel

I have a Route as below that will display a profile depending on the data in the url:
Route::get('/{region}/{summonername}', function () {
return 'Summoner Profile';
});
I have a Form on the Home page which consists of a Input Box and Region Selector. I am posting this data to:
Route::post('/summoner/data');
The problem is that i don't know how i can convert the form data eg. Summoner Name and Region into the url format where the user will be displayed with the profile page and the url would be /{region}/{summonername}. Am i supposed to use a Redirect::to inside my controller? I feel like that is a crappy way of doing it. Any Suggestions?
Right now when i post the data the url displays as '/summoner/data'.
I hope this makes sense, let me know if you need more clarification.
Routes :
Route::post('/summoner/data','ControllerName#FunctionName');
Route::get('/{region}/{summonername}', function () {
return view('SummonerProfile');
});
Controller:
public function FunctionName()
{
$SummonerName = Input::get('SummonerName');
$Region = Input::get('Region');
return Redirect::to('/{$Region}/{$SummonerName}');
}
Hope this will work. Try it!
Using Routes:
Route::post('/summoner/data',function () {
$SummonerName = Input::get('SummonerName');
$Region = Input::get('Region');
return Redirect::to('/{'.$Region.'}/{'.$SummonerName.'}');
});
Route::get('/{region}/{summonername}', function () {
return view('SummonerProfile');
});
Yes, you will need to redirect:
Route::post('/summoner/data', function (Request $request) {
return redirect()->url($request->region .'/'. $request->summonername);
});
If you want to take the data from URL, just do the following
use Illuminate\Http\Request;
Route::post('/summoner/data', function (Request $request) {
echo $request->segment(1); // gives summoner
echo $request->segment(2); // gives data
});

Laravel : use parameter that taken from url for dynamic table name

In controller, I want to use dynamic table name that taken from url and then show data using package chumper/datatable.
When I browse mysite.com/unit/rentin/send I have an AJAX error because the used table name is send_{unit} whereas my registered table name is send_rentin.
Here is my code:
routes.php
Route::resource('unit.send', 'SendController');
SendController.php
class SendController extends \BaseController {
public function index($unit)
{
$table = new Send; // Send is my model
$table->setTable('send_' .$unit);
if (Datatable::shouldHandle())
{
return Datatable::collection($table::all())
->showColumns('admin_no')
->addColumn('admin_date', function ($model) {
return date('d M Y', strtotime($model->admin_date));
})
->make();
}
return View::make('send.index')->withUnit($unit);
}
}
}
What do I miss?
After some hour try, I found a mistake at my views.
Wrong
{{ Datatable::table()
->addColumn('no', 'date')
->setUrl(route('unit.send.index'))
->render('datatable.uikit') }}
Right
{{ Datatable::table()
->addColumn('no', 'date')
->setUrl(route('unit.send.index', ['unit'=>$unit]))
->render('datatable.uikit') }}

Laravel 5: Call to a member function sum() on null and other persistence issues

I have a threaded-comments list each with an upvote/downvote button using jquery.upvote.js.
The voting action is done through ajax and it works, making an upvote or a downvote gets registered in the database with the correct values. Removing the vote will delete the record from the database. So technically it works as intended.
However, I have 2 problems:
The votes that the user has made do not persist after page reload which is important otherwise users won't know what they voted on.
When I add this chunk of code {{ $each_comment->commentvotes->sum('value') }} to the view to grab the sum of the votes on a given comment, I get the following error:
Call to a member function sum() on null
Routes
Route::resource('votes', 'VotesController');
Route::resource('commentvotes', 'CommentVotesController');
I'd like to point out that I've used the same method successfully on posts' votes with Vote model and VoteController.
CommentVote model
class CommentVote extends Model
{
protected $table = 'commentvotes';
protected $fillable = [
'value',
'comment_id',
'user_id'
];
public function user() {
return $this->belongsTo('App\User');
}
public function posts() {
return $this->belongsTo('App\Comment');
}
}
CommentVotesController
class CommentVotesController extends Controller
{
public function __construct() {
$this->middleware('auth', ['only' => ['create', 'edit'] ]);
}
public function store(Requests\CommentVoteRequest $request)
{
$commentId = $request->input('commentId');
$userId = $request->user()->id;
$value = $request->input('value');
// Check to see if there is an existing vote
$vote = CommentVote::whereCommentId($commentId)->whereUserId($userId)->first();
if (!$vote)
{
// First time the user is voting
CommentVote::create(['comment_id' => $commentId, 'user_id' => $userId, 'value' => $value]);
} else {
$vote->value == $value ? $vote->delete() : $vote->update(['value' => $value]);
}
// AJAX JSON RESPONSE
return response()->json(['status' => 'success',
'msg' => 'Vote has been added.']);
}
}
Javascript
$(document).ready(function() {
$('.topic').upvote();
$('.comment').upvote();
$('.vote').on('click', function (e) {
e.preventDefault();
var $button = $(this);
var postId = $button.data('post-id');
var value = $button.data('value');
$.post('http://localhost/r2/public/votes', {postId:postId, value:value}, function(data) {
if (data.status == 'success')
{
// Do something if you want..
}
}, 'json');
});
$('.commentvote').on('click', function (e) {
e.preventDefault();
var $button = $(this);
var commentId = $button.data('comment-id');
var value = $button.data('value');
$.post('http://localhost/r2/public/commentvotes', {commentId:commentId, value:value}, function(data) {
if (data.status == 'success')
{
// Do something if you want..
}
}, 'json');
});
});
Relevant part of the view comment_list.blade.php
#foreach($comments as $each_comment)
<div class="col-md-1">
<div class="upvote comment" data-comment="{{ $each_comment->id }}">
<a class="upvote commentvote {{ $each_comment->commentvotes && $each_comment->commentvotes->contains('user_id', Auth::id()) ? ($each_comment->commentvotes->where('user_id', Auth::id())->first()->value > 0 ? 'upvote-on' : null) : null}}" data-value="1" data-comment-id="{{ $each_comment->id }}"></a>
<!-- Notice how we set the sum of the votes for this post here -->
<span class="count">{{ $each_comment->votes->sum('value') }}</span>
<a class="downvote commentvote {{ $each_comment->commentvotes && $each_comment->commentvotes->contains('user_id', Auth::id()) ? ($each_comment->commentvotes->where('user_id', Auth::id())->first()->value < 0 ? 'downvote-on' : null) : null}}" data-value="-1" data-comment-id="{{ $each_comment->id }}"></a>
</div>
</div>
#endforeach
You have to define the relation inside the Comments model. Otherwise the CommentVotes will not be accessible to Comment entities.

Yii2, custom validation: clientValidateAttribute() doesn't work correctly

I have form, created by ActiveForm widget. User enters polish postal code there. In appropriate controller I put entered data in DB, for example:
$company_profile_data->postal_code = $_POST['CompanyProfiles']['postal_code'];
$company_profile_data->update();
I decided to use standalone validator for postal code validation. Rules for this attribute in model:
public function rules() {
return [
//...some other rules...
['postal_code', 'string', 'length' => [6,6]],
['postal_code', PostalValidator::className()], //standalone validator
];
}
app/components/validators/PostalValidator class code:
namespace app\components\validators;
use yii\validators\Validator;
use app\models\CompanyProfiles;
use app\models\Users;
class PostalValidator extends Validator {
public function init() {
parent::init();
}
public function validateAttribute($model, $attribute) {
if (!preg_match('/^[0-9]{2}-[0-9]{3}$/', $model->$attribute))
$model->addError($attribute, 'Wrong postal code format.');
}
public function clientValidateAttribute($model, $attribute, $view) { //want js-validation too
$message = 'Invalid status input.';
return <<<JS
if (!/^[0-9]{2}-[0-9]{3}$/.test("{$model->$attribute}")) {
messages.push("$message");
}
JS;
}
}
So, an example of correct code is 00-202.
When I (in user role) enter incorrect value, page reloads and I see Wrong postal code format. message, although I redefined clientValidateAttribute method and wrote JS-validation, which, as I suggested, will not allow page to reload. Then I press submit button again: this time page doesn't reload and I see Invalid status input. message (so, the second press time JS triggers). But I when enter correct code after that, I still see Invalid status input. message and nothing happens.
So, what's wrong with my clientValidateAttribute() method? validateAttribute() works great.
UPDATE
Snippet from controller
public function actionProfile(){ //can't use massive assignment here, cause info from 2 (not 1) user models is needed
if (\Yii::$app->user->isGuest) {
return $this->redirect('/site/index/');
}
$is_user_admin = Users::findOne(['is_admin' => 1]);
if ($is_user_admin->id == \Yii::$app->user->id)
return $this->redirect('/admin/login/');
$is_user_blocked = Users::find()->where(['is_blocked' => 1, 'id' => \Yii::$app->user->id])->one();
if($is_user_blocked)
return $this->actionLogout();
//3 model instances to retrieve data from users && company_profiles && logo
$user_data = Users::find()->where(['id'=>\Yii::$app->user->id])->one();
$user_data->scenario = 'update';
$company_profile_data = CompanyProfiles::find()->where(['user_id'=>Yii::$app->user->id])->one();
$logo = LogoData::findOne(['user_id' => \Yii::$app->user->id]);
$logo_name = $logo->logo_name; //will be NULL, if user have never uploaded logo. In this case placeholder will be used
$upload_logo = new UploadLogo();
if (Yii::$app->request->isPost) {
$upload_logo->imageFile = UploadedFile::getInstance($upload_logo, 'imageFile');
if ($upload_logo->imageFile) { //1st part ($logo_data->imageFile) - whether user have uploaded logo
$logo_file_name = md5($user_data->id);
$is_uploaded = $upload_logo->upload($logo_file_name);
if ($is_uploaded) { //this cond is needed, cause validation for image fails (?)
//create record in 'logo_data' tbl, deleting previous
if ($logo_name) {
$logo->delete();
} else { //if upload logo first time, set val to $logo_name. Otherwise NULL val will pass to 'profile' view, and user wont see his new logo at once
$logo_name = $logo_file_name.'.'.$upload_logo->imageFile->extension;
}
$logo_data = new LogoData;
$logo_data->user_id = \Yii::$app->user->id;
$logo_data->logo_name = $logo_name;
$logo_data->save();
}
}
}
if (isset($_POST['CompanyProfiles'])){
$company_profile_data->firm_data = $_POST['CompanyProfiles']['firm_data'];
$company_profile_data->company_name = $_POST['CompanyProfiles']['company_name'];
$company_profile_data->regon = $_POST['CompanyProfiles']['regon'];
$company_profile_data->pesel = $_POST['CompanyProfiles']['pesel'];
$company_profile_data->postal_code = $_POST['CompanyProfiles']['postal_code'];
$company_profile_data->nip = $_POST['CompanyProfiles']['nip'];
$company_profile_data->country = $_POST['CompanyProfiles']['country'];
$company_profile_data->city = $_POST['CompanyProfiles']['city'];
$company_profile_data->address = $_POST['CompanyProfiles']['address'];
$company_profile_data->telephone_num = $_POST['CompanyProfiles']['telephone_num'];
$company_profile_data->email = $_POST['CompanyProfiles']['email'];
$company_profile_data->update();
}
if (isset($_POST['personal-data-button'])) {
$user_data->username = $_POST['Users']['username'];
$user_data->password_repeat = $user_data->password = md5($_POST['Users']['password']);
$user_data->update();
}
return $this->render('profile', ['user_data' => $user_data, 'company_profile_data' => $company_profile_data, 'upload_logo' => $upload_logo, 'logo_name' => $logo_name]);
}
My inaccuracy was in clientValidateAttribute() method. Instead of $model->$attribute in code snippet:
if (!/^[0-9]{2}-[0-9]{3}$/.test("{$model->$attribute}")) {
...I had to use predefined JS-var value, cause this var changes with entered value change. So, my new code is:
public function clientValidateAttribute($model, $attribute, $view) {
return <<<JS
if (!/^[0-9]{2}-[0-9]{3}$/.test(value)) {
messages.push("Wrong postal code format.");
}
JS;
}
Model does not load rules and behaviors until not called any function from model. When you call $company_profile_data->update(); model call update and validate functions.
Try add after $company_profile_data = CompanyProfiles::find() this code:
$company_profile_data->validate();
Or just use load function. I think it will help.

Categories