Using Sentry with Codesleeve Stapler? - laravel

Update #1
So I think I found out where the problem is but since I lack the knowledge, there's no way for me to solve it.
When using Sentry, I've tried extending both 'Model' which is default with the Sentry package and Laravel's 'Eloquent' under Illuminate\Database\Eloquent\Model. Neither worked. But while using Laravel's Eloquent on its own (no Sentry package addons), Stapler works nicely.
So I guess there is some compatibilty problem between Stapler and Sentry's User model even though it uses Eloquent (its extra functionalities might be conflicting with stapler's traits or the construct function, I don't know). Too bad since Sentry is a great package for managing authentication and throttling and is widely used as far as I know.
Is there any easy way to use stapler's agnostic version with sentry or will I have to rewrite all my code to use laravel's auth?
Main question
I'm building an app which uses Cartalyst's Sentry for managing users. However, I want users and other models to be able to have associated avatars, so I'm using Codesleeve's Stapler too.
I've read both laravel-stapler's readme and most of stapler's standalone version documentation but couldn't figure out how to make them work with Sentry. Whenever I try to create an user through this function:
$user = Sentry::createUser(array(
'username' => Input::get('username'),
'email' => Input::get('email'),
'password' => Input::get('password'),
'activated' => false,
'avatar' => Input::file('avatar')
));
return 'success';
I get:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'avatar' in 'field list'
So it seems Sentry is querying as if the avatar is a string and Stapler is not working.
Here is my Sentry user model (which I'm extending in Laravel's):
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Illuminate\Database\Eloquent\Model;
use Codesleeve\Stapler\ORM\StaplerableInterface;
use Codesleeve\Stapler\ORM\EloquentTrait;
use Cartalyst\Sentry\Groups\GroupInterface;
use Cartalyst\Sentry\Hashing\HasherInterface;
use Cartalyst\Sentry\Users\LoginRequiredException;
use Cartalyst\Sentry\Users\PasswordRequiredException;
use Cartalyst\Sentry\Users\UserAlreadyActivatedException;
use Cartalyst\Sentry\Users\UserExistsException;
use Cartalyst\Sentry\Users\UserInterface;
class User extends Model implements UserInterface, StaplerableInterface {
use SoftDeletingTrait, EloquentTrait;
protected $fillabe = array('username','email','password','password_temp','code','active','avatar');
public function __construct(array $attributes = array()) {
$this->hasAttachedFile('avatar', [
'styles' => [
'medium' => '300x300',
'thumb' => '100x100'
]
]);
parent::__construct($attributes);
}
protected $dates = ['deleted_at'];
And inside Sentry's config:
'model' => 'Cartalyst\Sentry\Users\Eloquent\User',
This might be simple to solve since I'm pretty new into Laravel, Sentry and Stapler but I couldn't find any info which would make me solve the problem.
Thanks in advance!

Yes, it can be done. Based on what's in your OP, the root cause appears to be that your user model doesn't know that avatar is a Stapler-provided column.
Theory: the Sentry User Provider manufactures a user model, right here. That user model must be Stapler-aware, per the Stapler docs. Fortunately, Sentry is configurable and you can tell it to use your custom model with setModel().
Rough outline of solution: First, you need a model that is Stapler-aware. You might already have this. Note that it extends the Sentry-provided model class and also imports the Stapler trait. Both are important.
<?php // app/models/UserModel.php
namespace App\Model;
use Codesleeve\Stapler\ORM\StaplerableInterface;
use Codesleeve\Stapler\ORM\EloquentTrait;
use Cartalyst\Sentry\Users\Eloquent\User as SentryUserModel;
class UserModel extends SentryUserModel implements StaplerableInterface {
use EloquentTrait;
}
Second, you need to instruct Sentry to use this. Below I use run-time configuration changes, as is my preference. But you can also change the Sentry configuration file:
<?php // app/bootstrap.php
\Sentry::getUserProvider()->setModel('App\Model\UserModel');
I'm doing this from memory on Laravel 4, so there may need to be some tweaking involved.

Related

Where to save different models at once in Laravel?

I have a litte question for you.
I'm using Laravel and I'm not sure which is the best way (and place) to save different models at same time.
For example:
When a user creates a "RecordSheet", I need to automatically create other models related to the RecordSheet model. Obviously I will create the RecordSheet model in his own controller:
class RecordSheetController extends Controller
{
public function store(){
RecordSheet::create([
.......
'user_id' => Auth::user()->id
]);
}
}
Where should I put the creation of the other models? In the same RecordSheetController?
class RecordSheetController extends Controller
{
public function store(){
DB:beginTranaction()
try{
$record = RecordSheet::create([
.......
'user_id' => Auth::user()->id
]);
ModelB::create([
.......
'recordSheet' => $record->id,
'user_id' => Auth::user()->id
]);
}catch(Exception $e)
{
DB:rollback();
}
DB:committ();
}
}
I'm not sure about thate since I suppose that RecordSheetController should be responsible only of "RecordSheet" models and not other models.
Any suggestion would be appreciated! Thanks everyone!
you can use Laravel Observers for this scenario, create a RecordSheetObserver and place your ModelB code in the created method
Laravel provides some built-in conventions for placement of your action or CRUD (Create - Read - Update - Delete) code.
Typically, you can put the related model action in the same method. To start, you can utilise the artisan command:
php artisan make:controller RecordSheetController --resource
This will add the standard methods to your controller. These methods tie into any resource methods you have in your routing, which follow standards for GET/POST/PUT/etc.
Once you have your controller set up, it is usually easiest and most readable to do your related action within the same method, so you don't have to go back and forth with the user from page to controller and back again. So:
public function store(Request $request){
// Add transactions as you wish
$record = RecordSheet::create([
.......
' user_id' => Auth::user()->id
]);
ModelB::create([
.......
'recordSheet' => $record->id,
'user_id' => Auth::user()->id
]);
}
You can certainly make sub functions within this, but the key is to perform this at one time for efficiency. If there are many repeatable sub functions with less related actions, it may be helpful to move this to other parts of your app. But for simple, directly related creation, it tends to be more readable to keep them in the same class.

How does Laravel generate SQL?

I'm brand new to Laravel and am working my way through the Laravel 6 from Scratch course over at Laracasts. The course is free but I can't afford a Laracasts membership so I can't ask questions there.
I've finished Section 6 of the course, Controller Techniques, and am having unexpected problems trying to extend the work we've done so far to add a few new features. The course has students build pages that let a user show a list of articles, look at an individual article, create and save a new article, and update and save an existing article. The course work envisioned a very simple article containing just an ID (auto-incremented in the database and not visible to the web user), a title, an excerpt and a body and I got all of the features working for that. Now I'm trying to add two new fields: an author name and a path to a picture illustrating the article. I've updated the migration, rolled back and rerun the migration to include the new fields and got no errors from that. (I also ran a migrate:free and got no errors from that.) I've also updated the forms used to create and update the articles and added validations for the new fields. However, when I go to execute the revised create code, it fails because the SQL is wrong.
The error message complains that the author field doesn't have a default, which is true, I didn't assign a default. However, I did give it a value on the form. What perplexes me most is the SQL that it has generated: the column list doesn't show the two new columns. And that's not all: the values list is missing apostrophes around any of the string/text values. (All of the columns are defined as string or text.)
As I said, I'm completely new to Laravel so I don't know how to persuade Laravel to add the two new columns to the Insert statement nor how to make it put apostrophes around the strings in the values list. That hasn't come up in the course and I'm not sure if it will come up later. I was hoping someone could tell me how to fix this. All of my functionality was working fine before I added the two new fields/columns.
Here is the error message:
SQLSTATE[HY000]: General error: 1364 Field 'author' doesn't have a default value (SQL: insert into `articles` (`title`, `excerpt`, `body`, `updated_at`, `created_at`) values (Today in Canada, The ideal winter-beater, This car is the ideal winter-beater for the tough Canadian climate. It is designed to get you from A to B in style and without breaking the bank., 2020-02-15 17:37:54, 2020-02-15 17:37:54))
Here is ArticlesController:
<?php
namespace App\Http\Controllers;
use App\Article;
use Illuminate\Http\Request;
class ArticlesController extends Controller
{
public function index()
{
$articles = Article::latest()->get();
return view ('articles.index', ['articles' => $articles]);
}
public function show(Article $article)
{
return view('articles.show', ['article' => $article]);
}
public function create()
{
return view('articles.create');
}
public function store()
{
//Stores a NEW article
Article::create($this->validateArticle());
return redirect('/articles');
}
public function edit(Article $article)
{
return view('articles.edit', ['article' => $article]);
}
public function update(Article $article)
{
//Updates an EXISTING article
$article->update($this->validateArticle());
return redirect('/articles/', $article->id);
}
public function validateArticle()
{
return request()->validate([
'title' => ['required', 'min:5', 'max:20'],
'author' => ['required', 'min:5', 'max:30'],
'photopath' => ['required', 'min:10', 'max:100'],
'excerpt' => ['required', 'min:10', 'max:50'],
'body' => ['required', 'min:50', 'max:500']
]);
}
public function destroy(Article $article)
{
//Display existing record with "Are you sure you want to delete this? Delete|Cancel" option
//If user chooses Delete, delete the record
//If user chooses Cancel, return to the list of articles
}
}
Is there anything else you need to see?
It may be possible because of you don't have defined that column in fillable property, to use mass assignment you have to specify that columns.
Try after adding that columns in fillable property.
Laravel mass assignment
Hope this helps :)

changing key names when leveraging laravel resources to return API data

So I am currently building out an API and see that laravel has added an API resource which I am assuming is in lieu of something like fractal?
However running into an issue where when I go to return a collection using the XyzCollection resource I do not have the ability to change my DB names? So i end up with the same naming convention I have in my DB..
Plenty of obvious reasons why I don't want to have that be the case (i can go into them if need be) HOWEVER - is there a way to alter those key names?
for example in one of my tables I have a id,uuid,user_uuid
Now in fractal I would just transform these fields like so -
'id' => $xyz->uuid,
'user' => [
'data' => [
[
'user_id' => $xyz->user->uuid,
'username' => $xyz->user->username,
]
]
],
How do i do this when all i can do is pass in the collectionin the XyzCollection?
'data' => $this->collection,
and before you say use XyzResource ... When I am returning all(); records like I need to do plenty of times in an API (or paginate) etc etc. I can only do that from XyzCollection!
Thanks in advance
Steve
You first need to define a structure for a singular JsonResource object:
(php artisan make:resource Xyz)
public function toArray($request)
{
return [
'user_id' => $this->uuid,
'username' => $this->username,
];
}
And then tell your XyzCollection to use the class:
Customizing The Underlying Resource Class
Typically, the
$this->collection property of a resource collection is automatically
populated with the result of mapping each item of the collection to
its singular resource class. The singular resource class is assumed to
be the collection's class name without the trailing Collection
string.
For example, UserCollection will attempt to map the given user
instances into the User resource. To customize this behavior, you may
override the $collects property of your resource collection
(From https://laravel.com/docs/5.7/eloquent-resources#concept-overview)
If your ResourceCollection doesn't do anything extra, you might not always need it. Every JsonResource can transform itself into a resource collection with the collection() method, e.g. UserResource::collection($users)

how to use Laravel Rinvex Attributes?

I'm trying to use https://github.com/rinvex/attributes, and can't understand how.
Docs are not clear for me and i need help.
After installing the package i've done next:
I have \App\Models\Product class that i want to make attributable.
So I put in model
use Rinvex\Attributes\Traits\Attributable;
class Product extends Model
{
use CrudTrait;
use Sluggable;
use Attributable;
/***/
In AppServiceProvider's boot():
app('rinvex.attributes.entities')->push(App\Models\Product::class);
Next - just like in docs - creating the attribute in Tinker console just like that:
app('rinvex.attributes.attribute')->create([
'slug' => 'size',
'type' => 'varchar',
'name' => 'Product Size',
'entities' => ['App\Models\Product'],
]);
In DB i see added entries in attributes_entries and attributes tables.
But when i try to call
$product->size = 50;
$product->save(); - got "not found field in Model".
What do i do wrong?
Did you register varchar type? As it mentioned in documentation,
Rinvex Attributes does NOT register any types by default (see https://github.com/rinvex/laravel-attributes#register-your-types)
So i suggest you to add this code in your service providers register method:
Attribute::typeMap([
'varchar' => \Rinvex\Attributes\Models\Type\Varchar::class,
]);
Don't forget to include this class
use Rinvex\Attributes\Models\Attribute

How to handle created_by and updated_by columns for multi auth guards in laravel 5.6

I'm using laravel 5.6 multi auth where i have 3 guards admins, professionals, users. I'm using this package https://github.com/richan-fongdasen/eloquent-blameable for created_by, updated_by fields.
I have articles table where admins can create a post and also professionals can create a post, which are viewed by the frontend users.
I have added the below code to the admin model and whenever i create a post the creared_by and updated_by(if i update) are filled in with the logged in admin user id.
public function blameable()
{
return [
'user' => \App\Admin::class,
'createdBy' => 'user_id',
'updatedBy' => 'user_id'
];
}
Now when i create a post from the professionals section i want the createdBy and updatedBy to be professional id. How to achieve this?
Do i need to create a separate model as professionalposts for the same table and use
public function blameable()
{
return [
'user' => \App\Professional::class,
'createdBy' => 'user_id',
'updatedBy' => 'user_id'
];
}
This is an important field as it
1. Displays the author name for the articles which makes users know who wrote the article.
2. Helps to list all the articles created by the professional in their dashboard.
I have different ideas in my head like created a extra column professional_id(nullable) which will be filled if posted from professional section and is set to null if posted from admin.
What is the best way to handle this?
You can use this simple library:
use Culpa\Traits\Blameable;
use Culpa\Traits\CreatedBy;
use Culpa\Traits\DeletedBy;
use Culpa\Traits\UpdatedBy;
use Illuminate\Database\Eloquent\Model
class Comment extends Model
{
use Blameable, CreatedBy, UpdatedBy;
protected $blameable = array('created', 'updated', 'deleted');
// Other model logic here
}
How to install:
https://github.com/nstapelbroek/culpa-laravel-5

Resources