Jump to content

Help with user profile pages


SamC
 Share

Recommended Posts

Following on from this post:

I got to work in trying to make this a reality. After studying the API for a bit plus reading examples, I got a sign up form going:

//views/signup.php
<?php namespace ProcessWire;

    function createUser($user, $email, $password) {
        // create user
        $u = new User();
        // set name
        $u->name = $user;
        // set email
        $u->email = $email;
        //set password
        $u->pass = $password;
        // assign role of "member"
        $u->addRole("member");
        // save new member
        $u->save();
        // if successful
        $success = true;

        return $u;
    }

    function createProfile($user) {
        // create profile page
        $p = new Page();
        // set template (can only be child of profile-index)
        $p->template = "profile-entry";
        // set page name
        $p->name = $user->name;
        // set page title
        $p->title = $user->name;
        // set page title
        $p->altTitle = "Profile page for " . $user->name;
        // set page reference to correpsonding user page
        $p->userRef = $user->id;
        // save page
        $p->save();
    }

    // input field validation
    wireIncludeFile("./vendor/vlucas/valitron/src/Valitron/Validator.php");

    // sanitize input field values
    $user = $sanitizer->userName($input->post->inputUsername);
    $email = $sanitizer->email($input->post->inputEmail);
    $password = $input->post->inputPassword;

    $v = new \Valitron\Validator(array(
        "user" => $user,
        "email" => $email,
        "password" => $password
        )
    );

    $v->rule("required", ["user", "email", "password"]);
    $v->rule("email", "email");
    $v->rule("lengthMin", "password", 6);

    // if form has been submitted
    if($input->post->sendMe) {
        if ($v->validate()) {

            // return user if exists
            $u = $users->get($user);

            // return user if email address exists
            $e = $users->get("email=$email");

            // if both user or email don't exist
            if (!($u->id) && !($e->id)) {

                // create user and profile
                createProfile(createUser($user, $email, $password));

                // return result of attempted login
                $u = $session->login($user, $password);

                // check if user now exists
                if (!is_null($u)) {
                    $session->redirect($pages->get(1037)->url . $user);
                }
            }
            else {
                $session->success = false;
                // email taken
                if ($e->id) {
                    $session->flashMessage = 'Email already exists. Please use another email.';
                }
                // user taken
                else {
                    $session->flashMessage = 'User already exists. Please choose another username.';
                }
            }
        }
        else {
            $session->success = false;
            $session->flashMessage = 'All fields must be complete.';
        }
    }
?>


<div class="container">
    <div class="row justify-content-center py-5">
        <div class="col-6">

        <?php if($session->flashMessage):?>
            <div class="alert <?= $session->success ? 'alert-success' : 'alert-danger'?>" role="alert">
                <?php echo $session->flashMessage;?>
            </div>
        <?php endif;?>

        <form action="./" method="post">
            <div class="form-group <?php echo $v->errors('user') ? 'has-danger' : ''?>">
                <input type="username" class="form-control" name="inputUsername" placeholder="Username" value="<?= $user; ?>">
            </div>
            <div class="form-group <?php echo $v->errors('email') ? 'has-danger' : ''?>">
                <input type="email" class="form-control" name="inputEmail" placeholder="Email" value="<?= $email; ?>">
            </div>
            <div class="form-group <?php echo $v->errors('password') ? 'has-danger' : ''?>">
                <input type="password" class="form-control" name="inputPassword" placeholder="Password">
            </div>
            <div class="form-group">
                <button type="submit" name="sendMe" value="1" class="btn btn-primary">Create account</button>
            </div>
        </form>

        </div>
    </div>
</div>

<?php
    $session->remove('flashMessage');
?>

I've got templates:

profiles-index (just used in the tree to hold the profiles)
profile-entry (hold the individual profiles)

I put a page ref field on profile-entry, which points to the user. See question (3) below.

So, I end up with a URL like 'mysite.com/profiles/beastman'

 

So the questions I have:

1) Is there a better way of doing this:

// return user if exists
$u = $users->get($user);

// return user if email address exists
$e = $users->get("email=$email");

// if both user or email don't exist
if (!($u->id) && !($e->id)) {

Any advice on writing better code, then throw it my way please. I'm trying different techniques like using functions etc. for practice.

 

2) I haven't used this code, but what does:

$u->of(false);

...do? I see this in a lot of the login forms examples. i found it in the API but still not sure why to use it.


3) Have I done this the right way round? Should the page reference be on user template pointing to the profile, or the on the profile template pointing to the user?

 

The whole process goes something like this.

Go to /signup/:

1.thumb.png.a9d76b64bb593be5178f52127b388bb2.png

Validation, fields not filled out:

10.thumb.png.032c70f33ebd677987fdefbbd7669185.png

Validation, user already exists:

11.thumb.png.6b5b5e9bde4c667eda16cef04633a1ac.png

Validation, email already exists:

12.thumb.png.edb08d60e7ef605f376f8436b147e06f.png

Enter some details, logs you in and redirects:

4.thumb.png.4cfd41828b4271871b27e67b186f7bb4.png

Default login page:

5.thumb.png.c38859c29e36cd66eb474cc3494c329f.png

Refresh the admin login page:

6.thumb.png.20e5920fb1c09515d5b228b43cc4fe35.png

So now I can at last demonstrate my issues with users getting the tree view vs having a custom dashboard. Bear in mind they will only get to this page if they use the admin login form. If they go to the tree view here, I have another problem. If they click profile name (top right), then go to change the password, the rules are different from the ones I set in the initial sign up form. The built in rules are "Minimum requirements: at least 6 characters long, letter, digit." My rule was just 6 characters at least. Anyway, going off topic.

The "where does the user go and how do they change their profile details once they've logged in" bit part confuses me. Presuming they could go to a dashboard or something else if they used the 'other' login form block I'm working on and avoid the admin backend entirely:

7.thumb.png.ec3885398e83641910a19e6783e07dc9.png

So, the new user is in the system:

2.thumb.png.fed38765769b366b0a36d61c8d07c288.png

and:

3.thumb.png.fca55d7116c519c358c6e2c03c010e12.png

 

Sweet! Basic, pretty useless, but a great learning experience so far :lol: 

--EDIT--

Added some more images to show how the form works when validation fails. I would recommend recaptcha too though to stop spam users:

https://modules.processwire.com/modules/markup-google-recaptcha/

Once you've got a site key from Google, it's as simple as configuring the module in the PW admin then in your template:

$captcha = $modules->get("MarkupGoogleRecaptcha");

// render the recaptcha
<?= $captcha->render(); ?>

 

For valitron, install composer then (I ran this command from inside /site/templates/):

composer require vlucas/valitron

...results in '/site/templates/vendor' folder which has valitron inside. Then just include the file:

wireIncludeFile("./vendor/vlucas/valitron/src/Valitron/Validator.php");

Hopefully this will help someone :)

Edited by SamC
Added more details
  • Like 2
Link to comment
Share on other sites

4 minutes ago, SamC said:

So the questions I have:

1) Is there a better way of doing this:


// return user if exists
$u = $users->get($user);

// return user if email address exists
$e = $users->get("email=$email");

// if both user or email don't exist
if (!($u->id) && !($e->id)) {

Any advice on writing better code, then throw it my way please. I'm trying different techniques like using functions etc. for practice.

From what it looks like, $user is the currently logged in user - this check is outside your functions, so I don't see that this is actually doing anything.

The email check looks fine

This should also be ok, but remember the issue I mentioned with the first line.

7 minutes ago, SamC said:

2) I haven't used this code, but what does:


$u->of(false);

...do? I see this in a lot of the login forms examples. i found it in the API but still not sure why to use it.

This turns off output formatting which is important when saving fields to a page - consider a datetime - you want to save a unix timestamp, and not a formatted string like 14 September, 2017 7:17:11 am

9 minutes ago, SamC said:

3) Have I done this the right way round? Should the page reference be on user template pointing to the profile, or the on the profile template pointing to the user?

Really your call - there are arguments for both approaches. You might actually find things simpler if you try this approach to user pages: https://processwire.com/blog/posts/processwire-core-updates-2.5.14/#multiple-templates-or-parents-for-users which lets you set up users with URL accessible pages directly.

BTW - a great writeup of your approach here - I am sure others will learn lots!

  • Like 3
Link to comment
Share on other sites

I tend to do this sort of thing where I attach all the user based fields to the current user i.e. $user->fieldname instead fo creating a new page each time, then simply create a profile template shows those fields. which is fine if you have to be logged in, in your case you could enable page url partials in the profile tamplte and look for the partial (your user name input) to get the same effect.

  • Like 1
Link to comment
Share on other sites

31 minutes ago, adrian said:

From what it looks like, $user is the currently logged in user - this check is outside your functions, so I don't see that this is actually doing anything.

$user there was supposed to be checking whether the username used in the signup form, i.e. the current value of:

// sanitize input field values
$user = $sanitizer->userName($input->post->inputUsername);

...is already a username in the system. I forgot to mention my form checks whether a username exists, also whether an email exists. Usernames and emails must be unique. Maybe I should have used a different variable name to avoid confusion. Or maybe it doesn't do anything, have to check that.

35 minutes ago, adrian said:

This turns off output formatting which is important when saving fields to a page - consider a datetime - you want to save a unix timestamp, and not a formatted string like 14 September, 2017 7:17:11 am

Ah, that's useful to know, thanks.

35 minutes ago, adrian said:

Really your call - there are arguments for both approaches. You might actually find things simpler if you try this approach to user pages: https://processwire.com/blog/posts/processwire-core-updates-2.5.14/#multiple-templates-or-parents-for-users which lets you set up users with URL accessible pages directly.

I'll have to test this out, can't say I fully understood the writeup on that, would have to see it in action.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...