Authentication not working in yii2 - yii2-basic-app

I have Employee table whose structure is as follows:
EmpId
FirstName
LastName
Username
Password
Now in application component I have included my custom class as follows:
'user' => [
'identityClass' => 'app\models\Employee',
'enableSession' => true,
],
I have implemented foll methods in Employee model as follows:
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException();
}
public function getId()
{
return $this->EmpId;
}
public function getAuthKey()
{
throw new NotSupportedException();
}
public function validateAuthKey($authKey)
{
throw new NotSupportedException();
}
public static function findByUsername($username)
{
return self::findOne(['Username'=>$username]);
}
public function validatePassword($password)
{
return $this->Password === $password;
}
And in LoginForm model, I have following. Insted of User, I have included
custom class Employee.
public function getUser()
{
if ($this->_user === false)
{
$this->_user = Employee::findByUsername($this->username);
}
return $this->_user;
}
Now when I try to login and provide username and password, it gives the foll error:
Unknown Property – yii\base\UnknownPropertyException
Getting unknown property: app\models\Employee::username

Notice the Case in the column names in your - this should be the same as attributes in your model.
ie: replace -
$this->usename to $this->Username;

Related

REST API Custom Response query

I am a bit new to REST API. I have the below controller created to mock an API Service called by a client code under Test. I need to return the response in JSON format as mentioned in the query and need some help to fix it.
Controller:
[HttpPost]
[ApiKeyAuth]
[ValidateModel]
[ProducesResponseType(typeof(Item), StatusCodes.Status201Created)]
public async Task<IActionResult> AddNewItem([FromBody] Item item)
{
var itemId = await _repo.AddItemAsync(item);
return CreatedAtAction(nameof(GetItemById), new { itemId, controller = "Example" },
itemId);
}
Contract:
public interface IExampleControllerRepository
{
Task<int> AddItemAsync(Item item);
}
Repo:
public class ExampleRepository : IExampleControllerRepository
{
private readonly ExampleDbContext _context;
public ExampleRepository(ExampleDbContext context) => _context = context;
public async Task<int> AddItemAsync(Item item)
{
_context.Add(item);
await _context.SaveChangesAsync();
return item.ItemId;
}
}
Expected positive response template:
{"response":{"status":0,"data":[{"id":"1234"}]}}
// Summary:
// Creates a Microsoft.AspNetCore.Mvc.CreatedAtActionResult object that produces
// a Microsoft.AspNetCore.Http.StatusCodes.Status201Created response.
//
// Parameters:
// actionName:
// The name of the action to use for generating the URL.
//
// routeValues:
// The route data to use for generating the URL.
//
// value:
// The content value to format in the entity body.
//
// Returns:
// The created Microsoft.AspNetCore.Mvc.CreatedAtActionResult for the response.
[NonAction]
public virtual CreatedAtActionResult CreatedAtAction(string? actionName, object? routeValues, [ActionResultObjectValue] object? value)
{
throw null;
}
The response body's template is related to the type of value, So if you want to get the response template like:
{"response":{"status":0,"data":[{"id":"1234"}]}}
You need to pass a value of a specific type instead of itemId.
Here is a simple demo.
public class Test
{
public List<response> responses { get; set; }
}
public class response
{
public int status { get; set; }
public List<test1> data { get; set; }
}
public class test1
{
public string Id { get; set; }
}
For testing convenience, I just hard code here.
[HttpPost]
[ApiKeyAuth]
[ValidateModel]
[ProducesResponseType(typeof(Item), StatusCodes.Status201Created)]
public async Task<IActionResult> AddNewItem([FromBody] Item item)
{
Test test = new Test()
{
responses = new List<response>()
{
new response()
{
status = 0,
data = new List<test1> {
new test1()
{
Id = "1234",
}
}
}
}
};
var itemId = await _repo.AddItemAsync(item);
return CreatedAtAction(nameof(GetItemById), new { Id = item.ItemId }, test);
}
Result

Get distinct emails of users who commented on a ticket in Laravel

I have the following models.
Ticket
class Ticket extends Model
{
protected $fillable = ['title', 'content', 'slug', 'status', 'user_id'];
protected $guarded = ['id'];
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
public function comments()
{
return $this->hasMany('App\Comment', 'post_id', 'id');
}
public function commenters()
{
return $this->hasManyThrough('App\User', 'App\Comment');
}
}
Comment
class Comment extends Model
{
protected $guarded = ['id'];
public function user()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
public function ticket()
{
return $this->belongsTo('App\Ticket', 'post_id', 'id');
}
public function post()
{
return $this->morphTo();
}
}
User
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword;
protected $guarded = ['id'];
protected $hidden = [
'password', 'remember_token',
];
}
A ticket has many comments
A comment has one user
I'm trying to extract a name-string list of users who commented on a ticket, but with no success.
In my controller, I'm using the following code to extract the list of commenters.
Ticket::where('id', $comment->post_id)->commenters
However, I'm getting the error:
Property [commenters] does not exist on the Eloquent builder instance.
You're missing a closure. This query is unfinished:
Ticket::where('id', $comment->post_id)
And an unfinished query is an instance of the Builder class, not a Ticket instance, or Collection of Ticket instances as you're expecting.
If you're expecting a single Ticket instance, then you'd use ->first():
$ticket = Ticket::where('id', $comment->post_id)->with(['commenters'])->first();
$commenters = $ticket->commenters;
If you're expecting multiple Ticket instances, then you'd use ->get():
$tickets = Ticket::where('id', $comment->post_id)->with(['commenters'])->get();
foreach($tickets AS $ticket){
$commenters = $ticket->commenters;
}
Note: ->with(['commenters']) is used to speed up the loading of ->commenters; if you omit it, then a new query is run when you try to access $ticket->commenters.

REST API - CreatedAtRoute method doesn't return a value

I'm building some REST API server in .NET Core and using Postman software to test it. I have a problem with POST method which doesn't return me any value ("Could not get any response") when I try to perform second Add operation on my DBContext class inside CreateUser method. My code :
UsersController :
[Produces("application/json")]
[Route("api/[controller]")]
public class UsersController : Controller
{
private readonly DBContext _context;
#region CONSTRUCTOR
public UsersController(DBContext context)
{
_context = context;
}
#endregion
#region HTTP GET
// GET: api/users || api/users?cardnr=xxx
[HttpGet]
public async Task<IActionResult> GetUsers(string cardNr)
{
if (String.IsNullOrEmpty(cardNr))
{
try
{
var users = await _context.Users.ToListAsync();
if (users.Any())
{
return Json(users);
}
else
{
return NotFound();
}
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
else
{
try
{
var user = await _context.Users.FirstOrDefaultAsync(u => u.Cards.Any(c => c.CardNumber.Equals(cardNr)));
if (user == null)
{
return NotFound();
}
else
{
return new ObjectResult(user);
}
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
}
//GET: api/users/1
[HttpGet("{id}", Name = "GetUserByID")]
public async Task<IActionResult> GetUserByID(Int32 id)
{
try
{
var user = await _context.Users.FirstOrDefaultAsync(u => u.IDUser == id);
if (user == null)
{
return NotFound();
}
else
{
return new ObjectResult(user);
}
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
#endregion
#region HTTP POST
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] Models.User userToCreate, string userGroupID)
{
if (userToCreate == null)
{
return BadRequest();
}
else
{
try
{
_context.Users.Add(userToCreate);
int parsingResult;
// if user passed userGroupID
if (userGroupID != null)
{
// parsing if userGroupID is a number
if (!int.TryParse(userGroupID, out parsingResult))
{
return BadRequest();
}
else
{
// if client want to assign a new user to some group
if (parsingResult > 0)
{
// creating new record in UserGroup table - assigning a user to group
var userGroup = new Models.UserGroup();
_context.Entry(userGroup).Property("IDGroup").CurrentValue = parsingResult;
_context.Entry(userGroup).Property("IDUser").CurrentValue = userToCreate.IDUser;
_context.UserGroups.Add(userGroup); // NOTE HERE
}
}
}
await _context.SaveChangesAsync();
return CreatedAtRoute("GetUserByID", new { id = userToCreate.IDUser }, userToCreate);
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
}
#endregion
}
User model :
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Name { get; set; }
public List<UserGroup> UsersGroups { get; set; }
}
UserGroup model :
public class UserGroup
{
public Group Group { get; set; }
public User User { get; set; }
}
DBContext class :
public class DBContext : DbContext
{
public DBContext(DbContextOptions<DBContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// shadow property - foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDUser");
// shadow property - foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDGroup");
modelBuilder.Entity<UserGroup>()
.HasKey( new string[]{ "IDUser", "IDGroup" });
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.Group)
.WithMany(g => g.UsersGroups)
.HasForeignKey("IDGroup");
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.User)
.WithMany(u => u.UsersGroups)
.HasForeignKey("IDUser");
base.OnModelCreating(modelBuilder);
}
public DbSet<Group> Groups { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroups { get; set; }
}
The problem lies in HttpPost method in UsersController.
When I do "normal" POST and pass JSON object which contain a user to add without assigning it to group (empty userGroupID parameter) everything is ok - user gets added to the DataBase and Postman returns me a user with its ID.
screen :
and when I try to add a new user but with adding it to specific group I always get an error :
screen :
Even despite that error new user gets properly added to DB and associated with its group (record gets added to UserGroup table; UserGroup is join table between Users and Groups table). So I have proper data in my DB but I always get this error and I can't return new added user to client who called API and can't get his ID. Am I doing something wrong in my CreateUser method ?
UPDATE :
I have added a comment line in "NOTE HERE" in CreateUser method in UsersController. If I comment whole this line I don't get an error from Postman but obviously I don't get my User associated with its group (I don't get new record added to UserGroup join table). So it seems like another Add method on context object causing an error ... Does it make sense ?
Did you try to debug it?
Set a breakpoint on the row:
if (userToCreate == null)
Send again the request with Postman and debug your app. There you can see what and where it goes wrong.
Please let me know how it is going so I know how can I help you:)

Automapper : Mapping to an Interface

I am using AutoMapper to map between an entity and an Interface
First I created my mapping and checked it is valid.
AutoMapper.Mapper.CreateMap<User, IUserViewModel>();
AutoMapper.Mapper.AssertConfigurationIsValid();
Then I created a method that uses this mapping:
public IUserViewModel GetUser(int id)
{
var user= _userRepository.GetByKey(id);
var currentUser = Mapper.Map<User, IUserViewModel>(user);
return currentUser;
}
I am using this method in another place of my code
IUserViewModel myUser = XXXXX.GetUser(3);
This issue is this is myUser is always null.
However, when I debug my method and stop inside it juste before returning , I can see that my object currentSupplier is created and filled up correctly.
But when the method returns I get a null value.
I guess this has to do with the fact my object currentSupplier is created as
Proxy<....>
Any help?
Thank you.
Adding a full copy of my code that proves that the above works for me - couldnt fit it into the comments.
class Program
{
static void Main(string[] args)
{
Program program = new Program();
IUserViewModel myUser = program.GetUser(3);
Console.WriteLine(myUser.Name); // Prints Frank
Console.Read();
}
public Program()
{
Mapper.CreateMap<User, IUserViewModel>();
Mapper.AssertConfigurationIsValid();
}
private UserRepo _userRepository = new UserRepo();
public IUserViewModel GetUser(int id)
{
var user = _userRepository.GetByKey(id);
var currentUser = Mapper.Map<User, IUserViewModel>(user);
return currentUser;
}
}
public class UserRepo
{
public User GetByKey(int id)
{
return new User { Name = "Frank" };
}
}
public interface IUserViewModel
{
string Name { get; set; }
}
public class User
{
public string Name { get; set; }
}
Could you add some additional content to show where this is failing?

Serializability of enum-like class

I need to access an enum through a webservice.
As a webservice allocates 0 based integers to an enumeration (ignoring preset values in enum definition), I built the following:
public class StatusType
{
public StatusVal Pending { get { return new StatusVal( 1, "Pending"); } }
public StatusVal Authorised { get { return new StatusVal(2, "Authorised"); } }
public StatusVal Rejected { get { return new StatusVal(3, "Rejected"); } }
public StatusVal Sent { get { return new StatusVal(4, "Sent"); } }
public StatusVal InActive { get { return new StatusVal(5, "InActive"); } }
public List<StatusVal> StatusList()
{
List<StatusVal> returnVal = new List<StatusVal>();
StatusType sv = new StatusType();
returnVal.Add(sv.Pending);
returnVal.Add(sv.Authorised);
returnVal.Add(sv.Rejected);
returnVal.Add(sv.Sent);
returnVal.Add(sv.InActive);
return returnVal;
}
}
public class StatusVal
{
public StatusVal(int a, string b)
{
this.ID = a;
this.Name = b;
}
public int ID { get; set; }
public string Name { get; set; }
}
I then get the list of StatusVal with the following webmethod:
[WebMethod]
public List<ATBusiness.StatusVal> GetStatus()
{
ATBusiness.StatusType a = new ATBusiness.StatusType();
return a.StatusList();
}
I cannot however use this webmethod as referring it, I get the error: StatusVal cannot be serialized because it does not have a parameterless constructor.
I don't quite understand: should I pass params into the StatusValue type defined as the WebMethod's return Type?
I need this to return a list of StatusVals as per the StatusList() method.
As the error says, your class needs a constructor without parameters. When unserializing, the runtime will use that constructor instead of the one you have defined.
Something like:
public StatusVal()
{
}
When you created a constructor with parameters, you are automatically removing the default no-parameter constructor, and that's what the compiler is complaining about.