Find multiple elements of a sub-array entry - php

So, I'ld like to add a "Like" system on something I'm working on and I have a bit of trouble with it.
So my DB's table for my likes is like this :
========================================
|| *id* | created | user_id | post_id ||
========================================
I'm with with counting the number of likes for a post and all. The problem I have is I want to find if the logged user has already liked the post is is viewing so the "Like" link would become an "Unlike" link. And that's my problem. I can't manage to imagine how to do it.
Any help ?

Look at Model::hasAny() method:
$userHasLikedThisPost = $this->Like->hasAny(array(
'user_id' => $this->Auth->user('id'),
'post_id' => $postId
));
Then simply set a view var and output the corresponding link.
Make a helper method in the model so its more re-usable:
public function hasLike($postId, $userId) {
return $this->hasAny(array(
'user_id' => $userId,
'post_id' => $postId,
));
}

Related

CakePHP: Check for line item in iframe before submitting parent form

The app I'm working on (an order form) allows the user to enter multiple sub-records within an iframe. These sub-records are joined to the main record via a foreign key.
main_records line_items
----------- ----------
id int(11) PK etc. id int(11) PK etc.
main_record_id (FK)
I need the app to check whether at least one line item exists within this iframe before form submission. I would like to take advantage of the $validate functionality within the model, but I'm unsure how to proceed. Here's what I've tried in the Main model:
App::uses('AppModel', 'Model', 'LineItem');
public $hasMany = array(
'LineItem' => array(
'className' => 'LineItem',
'foreignKey' => 'main_record_id',
'dependent' => false
)
);
public $validate = array(
'main_record_id' = array(
'allowEmpty' => false,
'rule' => 'checkForLineItem',
'message' => 'You must enter at least one line item!'
)
);
//Check to make sure there is at least one line item before saving changes/submitting for approval
function checkForLineItem($id) {
$lines = $this->LineItem->find('all', array(
'fields' => array('LineItem.main_record_id'),
'conditions' => array('LineItem.main_record_id'=>$id, 'LineItem.deleted_record'=>0))
);
if(!empty($lines)) {
return true;
} else {
return false;
}
}
I also track whether the line item has been deleted. If it has, then it is not added to $lines.
I know I can accomplish this in the Controller, but as far as I know, that would require the form to post, and the user would lose any changes upon postback (I haven't yet implemented jQuery on this form). Am I on the right track with how to do this? What changes should I make to get this to work?
Your code looks about right, but validation indeed happens in form submit. If you want to check it prior to that you have to do in JavaScript (jquery). E.g. create a controller action that return if there are existing line items for given main record id and call it via AJAX.

What is 'The right way' to authorize this REST API request?

I'm building a REST Api and I'm sitting here with a problem. By no means I am an expert on this subject, so I want to learn how to address REST architecture the 'right way' (or at least in a way that makes sense).
I'm building a web application with a Angular Frontend and a laravel-based, RESTfull backend API. The app has these 3 tables: Albums, Posts and Comments. A user can write a post in an album if he/she is a member of that album.
A user can be invited to become member of an album and then see all it's posts and the comments for those posts. If an user isn't (invited to be) a member of an album it can't comment on posts in that album.
In other words: if a user comments on a post, the post has to be from an album the user is a member of.
My dilemma is: how do I check/authorize this request?
My Eloquent relationships are as follows:
The user table has a many to many relationship with albums
Albums have many posts
Posts have many comments
The incoming request is a POST request that has 2 parameters:
album_id (the album that the post is in)
post_id (for the post that is being commented on)
body (The actual comment itself)
The author for the post is retrieved via Auth::user();
My initial thoughts for addressing this problem are:
I check for all the albums a user is a member of
Build an array of al the ID's of the found albums the user is a member of
Check if the post_id parameter is in that array
If it's not, the user can't comment and if it is, the user can comment
My code so far:
// Validate the Request Body
$input = $this->request->only([ 'album_id', 'post_id', 'comment' ]);
$validator = Validator::make( $input, [
'album_id' => 'required|integer',
'post_id' => 'required|integer',
'comment' => 'required',
]);
// Authorize the Request
$albumIdList = Auth::user()->albums()->get()->pluck('id')->toArray();
$postIdList = Post::whereIn( 'album_id', $albumIdList )->select('id')->get()->toArray();
if( ! in_array($this->request->get('post_id'), $albumIdList))
return abort(403);
// Store the new comment
$comment = Comment::create([
'user_id' => Auth::user()->id,
'post_id' => $input['post_id'],
'comment' => $input['comment'],
]);
return $comment;
I think this is working properly, but what if a album has 1000 posts? Building the array wit all post ID's becomes really intensive for the server... How would a professional company (like Facebook, Twitter, Pinterest) tackle this in their web application?
Thanks in advance!
You're looking for the whereHas and exists methods:
$allowed = Auth::user()->albums()->whereHas('post', function ($query) {
$query->where($this->request->only('post_id'));
})->exists();
Also, there's no reason to pass in the album_id. Your code can be reduced to this:
$this->validate($this->request, [
'post_id' => 'required|integer',
'comment' => 'required',
]);
$allowed = Auth::user()->albums()->whereHas('posts', function ($query) {
$query->where($this->request->only('post_id'));
})->exists();
if (! $allowed) return abort(403);
$input = $this->request->only('post_id', 'comment');
return Comment::create($input + ['user_id' => Auth::id()]);
If you want to clean this up further, you should look into Laravel's authorization facilities.

Yiiframework First time login

I'm currently busy with a project that needs users to go to a specific page to create a profile when they log in for the first time (and haven't created one yet). Honestly, I don't know where to start. I would like to do it in a good way.
So in short:
User signs up -> logs in -> needs to fill in form before anything else is allowed -> continue to rest of application
Question: What is a neat way to do this? A solution that isn't going to give me problems in the future development of the application.
I suggest you to use filters. In every controller where the completed profile is neeeded add this code:
public function filters() {
return array(
'completedProfile + method1, method2, method3', // Replace your actions here
);
}
In your base controller (if you don't use base controller, in any controllers) you need to create the filter named completedProfile with the simular code:
public function filterCompletedProfile($filterChain) {
$criteria = new CDBCriteria(array(
'condition' => 'id = :id AND firstname IS NOT NULL AND lastname IS NOT NULL',
'params' => array(':id' => Yii::app()->user->getId())
));
$count = User::model()->count($criteria);
if ($count == 1) {
$filterChain->run();
} else {
$this->redirect(array('user/profile'));
}
}
Possibly add a field to the user profile database table which denotes if they have filled out their profile information. Something like profile_complete. Then you can do a test on pages to see if profile_complete is true and display the page if so, and display the profile page if not.

Codeigniter passing multiple parameters to controller from view?

I have a system that is outputting a number of images, with a A link next to them to set them as the album cover.
I have multiple albums, and in my database have a field called "is_feature" that is set to 1 if the image is the cover, and 0 if it isnt.
I don't know the best way of selecting the image, i originally outputted something like below;
Set
(image_id is the images id obviously), this function would call the model and set all other photos "is_feature" field to 0, and this photos "is_feature" to 1.
The problem is it is wiping all the other album features as well. I almost need to pass to variables in the A link, the first being the id of the image, the second being the id of the album, then my model function can only set "is_feature" to 0 where album_id = the id of the album passed.
Is there anyway to pass two variables like this? Or am i going about this in totally the wrong way?
You can set the values in the URL as query parameters
<a href="/admin/set_photo?var1=<?= $image_id;?>&var2=<?= $size;?>"
title="Set this photo as the feature photo"> Set </a>
Which you can retrieve in the controller
$image_id = $this->input->get('var1');
$image_size = $this->input->get('var2');
Uh what? You can pass whatever you need.
$data = array(
'title' => 'My Title',
'heading' => 'My Heading',
'message' => 'My Message'
);
$this->load->view('blogview', $data);
Depending upon data type as string or as array, there are 3 ways of passing data (You can use any of them explained below, BASED upon YOUR REQUIREMENT):
Through View
//For data you collected through POST method from FORM, collect them as array
$data=array(
'employee_name' => $this->input->post('emp_name'),
'employee_email' => $this->input->post('emp_email'),
'employee_password' => $this->input->post('emp_password')
);
$this->load-> view(''mynextpage", $data)
Through Controller
redirect('controller_name/index'.$valueofcustomer);
OR
redirect(base_url()."controller_name/index/".$valueofcustomer);
//then in your 'view', you can access value of customer like this:
$v_o_c = $this->uri->segment(3);
echo "your value is " .$v_o_c;
Through Session
$data = array(
'user_name' => $user_name,
'is_logged_in' => true
);
$this->session->set_userdata($data); //set the session
redirect('another_controller/index');
//then access those in another_controller like this:
$in = $this->session->set_userdata('$data');
Note: Session data will available only for redirect and lost on next page request

CakePHP view method using post title as url

I have the following link structure for my portfolio:
<?php echo $this->Html->link($post['Portfolio']['title'], array('controller' => 'portfolio', 'action' => 'view', Inflector::slug($post['Portfolio']['title'])), array('title' => $post['Portfolio']['title'])); ?>
Which gives urls like: http://driz.co.uk/portfolio/view/Paperview_Magazine
However how do I get my controller to show the item based on the title?
So far I have this but have not been able to get it to work and just get a blank page (so I ALSO need to check the format is correct and that their is a relevant item)
function view ( $title )
{
$posts = $this->Portfolio->find('first', array('conditions' => array('Portfolio.title' => $title)
));
if (empty($title))
{
$this->cakeError('error404');
}
$this->set(compact('posts'));
}
#Ross suggested that you search using Portfolio.slug so here's how you could do this:
Add a field to your database table called slug. You'll most likely want a VARCHAR with sufficient length to accommodate the slug.
When you create or update a "Portfolio" record, use the Inflector::slug method to generate a slug and save it to your database. You could always do this in the model's beforeSave event or if you prefer, in the controller before saving the data.
Update the find call to look for Portfolio.slug instead of Portfolio.title.
Unfortunately, there's no way to reverse the Inflector::Slug function as it removes certain characters like apostrophes, quotes, parentheses, etc. which is why you need to save the slug to your database if you want to search for it.
Here's how you could use the beforeSave event in your model:
public function beforeSave(array $options = array())
{
// If the title is not empty, create/update the slug.
if ( ! empty($this->data[$this->alias]['title'] )
$this->data[$this->alias]['slug'] = Inflector::slug($this->data[$this->alias]['title']);
// Returning true is important otherwise the save or saveAll call will fail.
return true;
}

Categories