In a Laravel application, I store details about a user in another table called profiles and I decided it would be a good idea to store one column as a simple JSON array.
This column is called socialProfiles
Here is my index method of ProfilesController
public function index()
{
$user = auth()->user();
if($user)
{
$profile = $user->profile;
if(!$profile)
{
// Define the social profiles array
$socialProfiles = [
'Facebook' => '',
'Twitter' => ''
];
$profile = new Profile();
$profile->user_username = $user->username;
$profile->mangedByUsername = $user->managedByUsername;
$profile->socialProfiles = json_encode($socialProfiles);
$profile->save();
}
$socialProfiles = json_decode($user->profile->socialProfiles, true);
return view('editable.people.profile', compact('user', 'socialProfiles'));
}
}
I set default values of nothing for both Facebook and Twitter and encode them as a JSON string, I then return the user and the profiles to the view.
In the view I have individual forms (within modals) that update different parts of a user profile, and they all go to the same method, because of this, my update function looks like this:
public function update(Request $request)
{
// TODO: Add validation before updating or adding the profile
$this->validate(request(), [
'background' => 'min:1',
'skills' => 'min:1',
]);
$user = auth()->user();
if($user)
{
// Only proceed if there is a logged in user
$profile = $user->profile;
// If there is no profile, create one for this user as they'll need one.
if (!empty(request()->get('background')))
{
$profile->background = $request->get('background');
}
if (!empty(request()->get('skills')))
{
$profile->skills = $request->get('skills');
}
if (!empty(request()->get('filepath')))
{
$profile->displayPicture = $request->get('filepath');
}
$profile->user_username = $user->username;
$profile->save();
return redirect()->back()->withSuccess('Your profile has been successfully updated');;
}
}
If I'm going to be updating the social profile JSON array would i need to recreate the array, encode it and then save it?
The reason for the separate modals is because the design looks like this:
Should I have many separate update methods?
What is your DBMS system, are you using Postgres JSON column?
If you are saving it like this $profile->socialProfiles = json_encode($socialProfiles);
You are saving it as JSON, which is fine. But if you need to update it, you have two choices, modify the original JSON column with some additions, or a create a new column socialProfileUpdates create a new JSON entry on this with the updated profile links. I won't recommend modifying the original JSON entry each time the user updates the record because this way your system would be really buggy.
Related
Why does sync makes duplicate sync in the pivot table if i selects more than an image ?
In my application when adding a new competition a user can select one or more images/documents and the file path will be saved in the files table and sync the data into the competition_file pivot table.
Here's the create UI
Here's the store function on my controller
public function store($competition, Request $request, Team $team)
{
if ($request->has('photos') && is_array($request->photos)) {
$files = $this->filesRepo->getByUuids($request->photos);
$fileId = $files->pluck('id')->toArray();
if ($files->isNotEmpty()) {
$forSync = array_fill_keys($fileId, ['competition_id' => $competition,'team_id' => $request->team,'type' => $request->type,'share_type' => $request->share_type]);
$team->documents()->sync($forSync);
}
}
return redirect(route('documents.index',$competition))->with('success', 'Document updated.');
}
Here's the relationship codes in my model
public function documents()
{
return $this->belongsToMany(File::class,'competition_file','team_id','file_id')->wherePivot('type', 'document');
}
When i selectes more than one image as below it makes a duplicate in the competition_file table
This is how it saves in the competition_file pivot table with a duplicate data
But if Dump the data before sync while I have selected two images it shows only two array see below codes
public function store($competition, Request $request, Team $team)
{
if ($request->has('photos') && is_array($request->photos)) {
$files = $this->filesRepo->getByUuids($request->photos);
$fileId = $files->pluck('id')->toArray();
if ($files->isNotEmpty()) {
$forSync = array_fill_keys($fileId, ['competition_id' => $competition,'team_id' => $request->team,'type' => $request->type,'share_type' => $request->share_type]);
dd($forSync);
$team->documents()->sync($forSync);
}
}
return redirect(route('documents.index',$competition))->with('success', 'Document updated.');
}
Result
And if I remove the Dump and reloads the same page it syncs correctly
And if I retry without Dump and if I selects two image and save it creates a duplicate ?
I need to know what might be creating the duplicate sync.
I hope my question is clear, can someone please help me out.
Just for the benefit of subsequent visitors.
public function store($competition, Request $request, Team $team)
{
if ($request->has('photos') && is_array($request->photos)) {
$files = $this->filesRepo->getByUuids($request->photos);
$fileId = $files->pluck('id')->toArray();
if ($files->isNotEmpty()) {
$forSync = array_fill_keys($fileId, [
'competition_id' => $competition,
'type' => $request->type,
'share_type' => $request->share_type
]);
//If the implicit route model binding is not working
//if $team is null or you need to explicitly set the team
//selected by user and which is not passed as route param
Team::findOrFail($request->team)->documents()->sync($forSync);
}
}
return redirect(route('documents.index',$competition))
->with('success', 'Document updated.');
}
This is function that is returning the view
// show individual post
public function showPost($id){
$targetPost = Post::findorFail($id);
$targetPost->increment('post_view_count');
$post = [
'post' => $targetPost,
'related_posts' => Post::all()->sortByDesc("id")->take(2)// load some related posts too
];
return view('post_single', $post);
}
What I am wanting here is to update the value by 1 when someone visits the page, the posts are in a list view to some other page, user are supposed to click that link.
Everything is working, but instead of incrementing the value by 1, it is incrementing by 2 even if I hit the reload button.
What I can assume is that the page is requested twice or loading via some kinda middleware first. That's why the request is being sent twice. How do I fix this?
Such a basic counter will not suit your needs. What you can do is create a migration for your Post Views:
Schema::create("post_views", function(Blueprint $table)
{
$table->unsignedInteger("id");
$table->unsignedInteger("id_post");
$table->string("session_id");
$table->string("user_id");
$table->string("ip");
$table->string("agent");
$table->timestamps();
});
Next create a model that will handle the views for you
class PostsViews extends \Eloquent {
protected $table = 'posts_views';
public static function createViewLog($post) {
$postsViews= new PostsViews();
$postsViews->id_post = $post->id;
$postsViews->titleslug = $post->titleslug;
$postsViews->url = \Request::url();
$postsViews->session_id = \Request::getSession()->getId();
$postsViews->user_id = \Auth::user()->id;
$postsViews->ip = \Request::getClientIp();
$postsViews->agent = \Request::header('User-Agent');
$postsViews->save();
}
Now use this in your showPost function():
public function showPost($id){
$targetPost = Post::findorFail($id);
PostsViews::createViewLog($targetPost);
$post = [
'post' => $targetPost,
'related_posts' => Post::all()->sortByDesc("id")->take(2)
];
return view('post_single', $post);
}
Now it will log all views but you can filter it out when you need it on User agent, IP address or session. You can also use that kind of filter when logging a view to just log 1 view per post per viewer.
When updating my Post model, I run:
$post->title = request('title');
$post->body = request('body');
$post->save();
This does not update my post. But it should according to the Laravel docs on updating Eloquent models. Why is my model not being updated?
I get no errors.
The post does not get updated in the db.
Besides not being updated in the db, nothing else seems odd. No errors. Behavior as normal.
Result of running this test to see if save succeeded was true.
This Laravel thread was no help
Post model:
class Post extends Model
{
protected $fillable = [
'type',
'title',
'body',
'user_id',
];
....
}
Post controller:
public function store($id)
{
$post = Post::findOrFail($id);
// Request validation
if ($post->type == 1) {
// Post type has title
$this->validate(request(), [
'title' => 'required|min:15',
'body' => 'required|min:19',
]);
$post->title = request('title');
$post->body = request('body');
} else {
$this->validate(request(), [
'body' => 'required|min:19',
]);
$post->body = request('body');
}
$post->save();
return redirect('/');
}
Bonus info
Running dd($post->save()) returns true.
Running
$post->save();
$fetchedPost = Post::find($post->id);
dd($fetchedPost);
shows me that $fetchedPost is the same post as before without the updated data.
Check your database table if the 'id' column is in uppercase 'ID'. Changing it to lower case allowed my save() method to work.
I had the same and turned out to be because I was filtering the output columns without the primary key.
$rows = MyModel::where('...')->select('col2', 'col3')->get();
foreach($rows as $row){
$rows->viewed = 1;
$rows->save();
}
Fixed with
$rows = MyModel::where('...')->select('primary_key', 'col2', 'col3')->get();
Makes perfect sense on review, without the primary key available the update command will be on Null.
I had the same problem and changing the way I fetch the model solved it!
Was not saving even though everything was supposedly working just as you have mentioned:
$user = User::find($id)->first();
This is working:
$user = User::find($id);
You have to make sure that the instance that you are calling save() on has the attribute id
Since Laravel 5.5 laravel have change some validation mechanism I guess you need to try this way.
public function store(Request $request, $id)
{
$post = Post::findOrFail($id);
$validatedData = [];
// Request validation
if ($post->type == 1) {
// Post type has title
$validatedData = $request->validate([
'title' => 'required|min:15',
'body' => 'required|min:19',
]);
} else {
$validatedData = $request->validate([
'body' => 'required|min:19',
]);
}
$post->update($validatedData);
return redirect('/');
}
Running dd() inside a DB::transaction will cause a rollback, and the data in database will not change.
The reason being, that transaction will only save the changes to the database at the very end. Ergo, the act of running "dump and die" will naturally cause the script to cease and no therefore no database changes.
Check your table if primary key is not id ("column name should be in small letters only") if you have set column name with different key then put code in your Model like this
protected $primaryKey = 'Id';
So this might be one of the possible solution in your case also if your column name contains capital letters.
Yes this worked for me fine,
You should have column names in small letter,
If you don't have then mention it in the model file, mainly for primaryKey by which your model will try to access database.
For use save () method to update or delete if the database has a primary key other than "id". need to declare the attribute primaryKey = "" in the model, it will work
Try this
public function store($id,Request $request)
{
$post = Post::findOrFail($id);
// Request validation
if ($post->type == 1) {
// Post type has title
$request->validate([
'title' => 'required|min:15',
'body' => 'required|min:19',
]);
$post->update([
'title' => request('title');
'body' => request('body');
]);
} else {
$request->validate([
'body' => 'required|min:19',
]);
$post->update([
'body' => request('body');
]);
}
return redirect('/');
}
In my experience, if you select an Eloquent model from the db and the primary_key column is not part of the fetched columns, your $model->save() will return true but nothing is persisted to the database.
So, instead of doing \App\Users::where(...)->first(['email']), rather do \App\Users::where(...)->first(['id','email']), where id is the primary_key defined on the target table.
If the (sometimes micro-optimization) achieved by retrieving only a few columns is not really of importance to you, you can just fetch all columns by doing \App\Users::where(...)->first(), in which case you do not need to bother about the name of the primary_key column since all the columns will be fetched.
If you using transactions.
Do not forget call DB::commit();
It must look like this:
try{
DB::beginTransaction();
// Model changes
$model->save();
DB::commit();
}catch (\PDOException $e) {
DB::rollBack();
}
I have the same issue although there are try / catch block in controller#action() but there were no response, it just stops at $model->save(); there is no log entry either in apache error.log or laravel.log. I have just wrapped the save() with try / cactch as follows, that helped me to figure out the issue
try{
$model->save();
}
catch (\PDOException $e) {
echo $e->getMessage();
}
I have been experiencing the same issue and found a workaround. I found that I was unable to save() my model within a function called {{ generateUrl() }} on my home.blade.php template. What worked was moving the save() call to the controller that returns the home.blade.php template. (IE, save()ing before the view is returned, then only performing read operations within {{ generateUrl() }}.)
I was (and am) generating a state to put in a URL on page load:
<!--views/home.blade.php-->
Add Character
Below is what did not work.
// Providers/EveAuth.php
function generateUrl()
{
$authedUser = auth()->user();
if (!$authedUser) {
return "#";
}
$user = User::find($authedUser->id);
$user->state = str_random(16);
$user->save();
$baseUrl = 'https://login.eveonline.com/oauth/authorize?state=';
return $baseUrl . $user->state;
}
This was able to find() the User from the database, but it was unable to save() it back. No errors were produced. The function appeared to work properly... until I tried to read the User's state later, and found that it did not match the state in the URL.
Here is what did work.
Instead of trying to save() my User as the page was being assembled, I generated the state, save()d it, then rendered the page:
// routes/web.php
Route::get('/', 'HomeController#index');
Landing at the root directory sends you to the index() function of HomeController.php:
// Controllers/HomeController.php
public function index()
{
$authedUser = auth()->user();
if ($authedUser) {
$user = User::find($authedUser->id);
$user->state = str_random(16);
$user->save();
}
return view('home');
}
Then, when generating the URL, I did not have to save() the User, only read from it:
// Providers/EveAuth.php
function generateUrl()
{
$authedUser = auth()->user();
$user = User::find($authedUser->id);
$baseUrl = 'https://login.eveonline.com/oauth/authorize?state=';
return $baseUrl . $user->state;
}
This worked! The only difference (as far as I see) is that I'm save()ing the model before page assembly begins, as opposed to during page assembly.
I'm working on L5.5 and I need to delete user but not his/her posts. So I basically need to assign his/her posts to another user which has to be Non-removable.
What I need:
Create a user which can't be deleted at least not from front-end even by owner of website but can be edited. (mostly is like bot for this application)
If I delete a user and that user had post(s) those post(s) remain and assign to this user (bot). It means this bot will become author of those posts.
Check for number 2 that only if user with post that happens if user has no post just delete him/her.
This is my usecontroller destroy method currently:
public function destroy($id)
{
$user = User::findOrFail($id);
Storage::delete($user->image);
$user->delete();
return redirect()->route('users.index')->with('flash_message', 'User successfully deleted');
}
Thanks.
According to your needs, you will require softDeletes in your User model and their respective tables in the database, now this solves your 1st problem where your not deleting the user from table simply adding deleted_at column.
Edit: As you are using Zizaco\Entrust\Traits\EntrustUserTrait you need to have your user model look something like this:
class User extends Model implements AuthenticatableInterface
{
use Authenticatable;
use EntrustUserTrait { restore as private restoreA; }
use SoftDeletes { restore as private restoreB; }
public function restore()
{
$this->restoreA();
$this->restoreB();
}
}
For more information about this error you need to look: https://github.com/Zizaco/entrust/issues/742
so now coming to the 2nd point, retrieving the post with deleted model can be used withTrashed() something like:
$user = User::withTrashed()->all();
$user = User::withTrashed()->where('id', 1);
$posts = $user->posts()->get();
// Or do your relational things
Even if you want to assign it to different user then you need to create a new user and apply update methods to all the relational model while deleting the user which seems a bad idea.
Edit:
So in this case you can have:
$oldUser = User::find($id);
$user = User::find($botID); // Find the bot user
$oldFoods = $oldUser->food()->get();
foreach($oldFoods as $food)
{
$food->user_id = $user->id;
$food->save();
}
Now for your 3rd point if the user has no post then you can do a small check something like this:
$user = User::find($request->id);
$posts = $user->posts()->get()->first();
if(isset($posts))
{
$user->delete();
}
else
{
$user->forceDelete();
}
Hope this justifies all your needs.
Conclusion So fnally you can have your destroy method in userController as:
public function destroy($id)
{
$user = User::findOrFail($id);
$foods = $user->food()->get();
if(isset($foods))
{
$botUser = User::where('username', '=', 'bot'); // Find the bot user
foreach($foods as $food)
{
$food->user_id = $botUser->id;
$food->save();
}
$user->delete();
}
else
{
$user->forceDelete();
}
Storage::delete($user->image);
return redirect()->route('users.index')->with('flash_message', 'User successfully deleted');
}
Edit your database with
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')
->onDelete('restrict')
->onUpdate('restrict');
just started using Laravel but want to make sure I am using it correctly.
Most of my work is CMS based so read / write / update etc to a database.
An example of what I have done so far is an insertion into the DB:
On the view I have a form with a URL of 'addNewUser'.
In my routes I then do:
Route::post('addnewuser', array('uses' => 'UserController#addNewUser'));
My user controller 'addNewUser' method is (simplified):
public function addNewUser() {
$data = Input::all();
$rules = array(
'username' => 'required|alpha_dash|max:16|unique:users,username',
);
$validator = Validator::make($data, $rules, $messages);
if ($validator->fails())
{
Input::flash();
$errors = $validator->messages();
return Redirect::to('/register')->withErrors($validator)->withInput();
}
$user = new User;
$user->save();
return Redirect::to('/login')->with('successLogin', '1');
}
Is this correct? I have read somewhere that all DB interaction should be in the model?
Likewise when reading from the DB to display a foreach for example, I do the following directly in the view:
$builds = DB::table('blogs')->orderBy('id', 'desc')->get();
if ($builds) {
foreach ($builds as $build)
{
$safeURLSlug = stringHelpers::safeURLSlug($build->blogtitle);
echo "
// stuff
";
}
} else {
// no stuff
}
Should I be doing these sort of queries and showing of data directly in the view? or in a model / controller function etc?
Want to check im doing things 100% correct / the standard way of doing things before I get too involved.
I can see a few things that I personally would have done differently.
For example I usually put $rules as a class variable so it can be used in different functions related to your Users.
Have you tested your code yet? Any errors?
In your addNewUser function does it save any data? I know you have "simplified" above the code snippet but there should be $user->username = $data['username']; etc. in between creating your $user variable and running $user->save();, so if you excluded this on purpose then I don't see anything else with your model.
In your view code, $builds = DB::table('blogs')->orderBy('id', 'desc')->get(); should be done in your controller and passed to your view like so return View::make('example', array('builds' => $builds))
I'd also change
$builds = DB::table('blogs')->orderBy('id', 'desc')->get();
to
$builds = Blog::orderby('id','desc')->get(); if you have a Blog model, otherwise your code is fine.
You could move:
$rules = array(
'username' => 'required|alpha_dash|max:16|unique:users,username',
);
to User model as static variable, and instead of:
$validator = Validator::make($data, $rules, $messages);
you could use:
$validator = Validator::make($data, User::$rules, $messages);
But definitely you shouldn't get data from database in your View, this code should be in controller, for example:
$builds = DB::table('blogs')->orderBy('id', 'desc')->get();
return View::make('someview')->with('builds', $builds);
of course if you have Blog model, you should use here:
$builds = Blog::orderBy('id', 'desc')->get();
return View::make('someview')->with('builds', $builds);
It's also unclear what the following code does:
$safeURLSlug = stringHelpers::safeURLSlug($build->blogtitle);
but probably you could move it to your Blog model and use accessor to make the change:
public function getSafeSlugAttribute($value) {
return stringHelpers::safeURLSlug($this->blogtitle);
}
and now your view could look like this:
#foreach ($builds as $build)
{{{ $build->title }}} {{{ $build->safeSlug }}}
#endforeach
I suggest you take a look on Laravel Generators.
https://github.com/JeffreyWay/Laravel-4-Generators
Install and then run:
php artisan generate:scaffold customer
Laravel line command generator create a basic CRUD for you with controller, model, views and database migrations. That's good to safe time and keep your project with some default organization.