Eloquent destroy() isn't deleting row in db - Laravel 5.6 - eloquent

I am trying to make a feature on my site to delete a row in the database for news articles and it doesn't seem to be working. Here is my Route and my Controller:
Route:
Route::get('/admin/news/delete/{id}', 'NewsController#destroy');
Controller:
public function destroy(News $id)
{
News::destroy($id);
return redirect('/admin/news')->with('status', 'News successfully deleted.');
}
It returns with the success message but the row never gets deleted in the database. What am i missing?

Since you are using route model binding, $id already contains a News instance (not an id).
You should probably rename it:
Route::get('/admin/news/delete/{news}', 'NewsController#destroy');
public function destroy(News $news)
{
$news->delete();
return redirect('/admin/news')->with('status', 'News successfully deleted.');
}
Also, it's recommended to use POST requests when you delete data.

Related

Eloquient with relation not working with find

I am new to laravel. I am facing a very weird problem. I have a model comment which is related to User model in laravel.
The Relationships are defined as such
//App\User model
public function comments()
{
return $this->hasMany('App\comment');
}
And
//App\Comment model
public function User()
{
return $this->belongsTo('App\User');
}
now when i am fetching user and comment s using find and with it is returning data for all the users in the table. My code is like this: App\User::find(1)->with('comments')->get(); Can some one tell me what am doing wrong?
Try something like this
$comments=App\User::whereId(1)->comments->get();
This should load every comment associated with user with ID 1
//App\User model
public function comments() {
return $this->hasMany('App\comment','user_id','id');
}
//In your controller
use App\User;
$comment = User::where('id',2)->comments->get();
//I hope It's work for you!

Entity Framework Core Nested Transaction or update DbContext among saveChanges()

I have this API which deletes and adds different users into the the Database. The way this API is written is something like this:
/* Assuming 'users' is an array of users to delete (if users[x].toDelete == true),
or to add otherwise */
using (var db = new myContext())
{
using (var dbTransaction = db.Database.BeginTransaction())
{
try
{
/* Performing deletion of Users in DB */
SaveChanges();
/* Performing add of Users in DB */
foreach(user in users)
if (!user.toDelete) {
db.users.add(..);
db.SaveChanges();
}
dbTransaction.Commit();
}
catch (ValidationException ex)
{
dbTransaction.Rollback();
}
}
}
The reason why I use a transaction is that I have some validations that should be run on the new data.. For example, I cannot add two user with the same email!
All the validation Server-Side is done with Data-Annotations and the MetaData classes, therefore I created the Attribute Uniqueness which I associated to the property email of the class UserMetaData.
So, the problem is that, inside the Uniqueness Attribute I need to check again the database to search for other users with the same email:
public class IsUnique : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
...
using (var db = new myContext()) {
/* Here I perform a select on the DB looking for records
with the same email, all using Reflection. */
if (recordFound > 0)
return new ValidationResult("email already exists");
return ValidationResult.Success;
}
}
}
So, as you can see, inside the validation I use new myContext() and that's where the problem is: let's pretend I have an empty database and I add two user with the same email in the same query.
As you can see, I call the db.SaveChanges() after every user has been added, therefore I thought that, when the second user to be added would have been validated, my validation would have known that the first user added and the second one have the same email, therefore an Validation Exception would have been throw and the dbTransaction.Rollback() called.
The problem is that inside the validator i call new myContext() and inside that context the changes that I thought would have been there thanks to the db.SaveChanges() there arent't, and that's I think is because all the changes are inside a Transaction.
Sooo.. The solutions I thought so far are two:
Nested Transaction: in the API I have an outer transaction which save the state of the DB at the beginning, then some inner transaction are run every time a db.SaveChanges() is called. Doing this, the validation does see the changes, but the problem is that, if in the catch i called outerTransaction.rollback(), the changes made by the inner transactions are not rollbacked;
Some way to retrieve the context I have used in the API even in the validation.. But in there I only have these two arguments, so I don't know if it's even possible;

REST Api with QueryParamAuth authenticator - Yii2

I'm trying to create rest api for my application to get the data in my android app. This is my controller
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
use yii\filters\auth\QueryParamAuth;
/**
* Tk103 Controller API
*/
class Tk103Controller extends ActiveController
{
public $modelClass = 'api\modules\v1\models\Tk103CurrentLocation';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => QueryParamAuth::className(),
];
return $behaviors;
}
}
I added access_token column in my user table, implemented findIdentityByAccessToken() in User Model and calling this URL
http://localhost:7872/api/v1/tk103s?access-token=abcd
This is working great and returning data if and only if access_token matches with any single user in the table.
I checked QueryParamAuth class and found that QueryParamAuth::authenticate() returns $identity after successful authentication.
Currently this url is returning whole data of my table.
What I want is(after authentication):
Get user id/username of the requester.
Based on that id/username, the data related to him as per relations of tables in db. (currently whole rows are being returned but I want only few that are matching with the current requester/user)
I tried but didn't getting any clue to catch returned $identity of user after authentication.
And I know it is possible too to make this work. Help me out folks to create magic.
Get user id/username of the requester.
That user instance you did return within the findIdentityByAccessToken method should be accessible any where inside your app within Yii::$app->user->identity. And should hold all the attributes retreived from DB. here is a quick example of using it to check access within the checkAccess method of the ActiveController class:
public function checkAccess($action, $model = null, $params = [])
{
// only an image owner can request the related 'delete' or 'update' actions
if ($action === 'update' or $action === 'delete') {
if ($model->user_id !== \Yii::$app->user->identity->id)
throw new \yii\web\ForbiddenHttpException('You can only '.$action.' images that you\'ve added.');
}
}
Note that the checkAccess is by default an empty method that is manually called inside all the built-in actions in ActiveController. the Idea is to pass the action ID and the model instance to it just after retrieving it from DB and before modifying it so we can do extra checks. If you just need to perform checks by actions ID then yii\filters\AccessControl may be enough but inside checkAccess you are expecting to also get the model instance itself so it is important to note that when building your own actions or overriding existing onces. be sure to manually invoke it the same way it is done in UpdateAction.php or DeleteAction.php.
whole rows are being returned but I want only few .. matching with .. current requester/user
It depends on how your data is structured. You can override ActiveController's actions to filter results before outputting them, it can be handled in the related SearchModel class if you are using one or it can be handled in model. A quick tip may be by simply overriding the find method inside your model:
public static function find()
{
return parent::find()->where(['user_id' => Yii::$app->user->getId()]); // or Yii::$app->user->identity->id
}
Note that this works only when using ActiveRecord. Which means when using this:
$images = Image::find()->all();
The find method we just overriden will be filtered by default by always including that where condition before generating the DB query. Also note the default built-in actions in ActiveController are using ActiveRecords but if you are using actions where you are constructing the SQL queries using the Query Builder then you should manually do the filtering.
The same can be done if using ActiveQuery (maybe better explained here) by doing this:
public static function find()
{
$query = new \app\models\Image(get_called_class());
return $query->andWhere(['user_id' => Yii::$app->user->getId()]);
}

oData's skip() and top() pulling entire record set before filtering

i have an oData enabled web api function
[EnableQuery()]
public IQueryable<StoreCommand> Get()
{
return _storeCommandService.GetAllStoreCommands().AsQueryable();
}
the service layer calling Mongodb based Repository pattern's implementation.
public IEnumerable<StoreCommand> GetAllStoreCommands()
{
return _uow.StoreCommands.GetAll();
}
where GetAll is implemented in Repository layer like
public IList<TEntity> GetAll()
{
return _collection.FindAllAs<TEntity>().ToList();
}
where _collection is a MongoCollection of c# driver.
when i make a call like
http://localhost:xxxx/api/storeCommandsrest?$skip=0&$top=10&$orderby=Name
i get top 10 records but it pulls all the records from the DB and send me back top 10.
Please guide how we can pull only the required set from the DB.
Comment moved to answer:
You aren't returning an IQueryable from GetAllStoreCommands(). Your return type must be an IQueryable(). To get that from the driver, it should be _collection.AsQueryable().

Why do I need to Insert and Update methods to my DomainService?

If I don't add Insert and Update methods to my domain service, I get exceptions when I try to add entities to the associated EntityCollection of my Entity. Now that I've added them (completely blank) I can add entities and modify them on the client but they never show up in my database. What am I missing? Do I need to create my own Insert and Update methods to my domain service and if so, what on earth would I put in them?
Edits:
This is what I have in my DomainContext. This seems a bit superfluous; I would think the Entity
Framework would already do this.
[Update]
public void UpdateProject(Project a_project)
{
ObjectContext.AcceptAllChanges();
}
[Update]
public void UpdateProjectItem(ProjectItem a_projectItem)
{
ObjectContext.AcceptAllChanges();
}
[Insert]
public void InsertProjectItem(ProjectItem a_projectItem)
{
ObjectContext.ProjectItems.AddObject(a_projectItem);
ObjectContext.AcceptAllChanges();
}
And this is how I'm using this on the client.
ProjectItem projectItem = new ProjectItem();
_reservedProject.Status = Project.ProjectStatusSubmitted;
_reservedProject.ProjectItems.Add(projectItem);
projectItem.LibraryItem = a_item;
_projectItems.Add(projectItem);
_domainContext.SubmitChanges();
UpdateProjectItem is never called.